package io.sloeber.core.toolchain;

import static io.sloeber.core.common.Const.*;

/**
 * This class is a copy of the cdt class GnuMakefileGenerator
 * It has been modified but these changes did not (yet) get into 
 * the source.
 * As such sometimes it is good to compare this file with the original.
 * I do this as follows
 * Copy the content of GnuMakefileGenerator in a java file you can compare
 * set your formatting to a formatting allowing easier compares
 * that is long lines
 * long comments
 * replace without regex in the copy
 * 		GnuMakefileGenerator with ArduinoGnuMakefileGenerator
 *      GnuDependencyGroupInfo with ArduinoGnuDependencyGroupInfo
 *      ManagedBuildGnuToolInfo with ArduinoManagedBuildGnuToolInfo
 *      SEPARATOR with FILE_SEPARATOR
 *      
 * replace with regex
 * 	"//.*" with "" (in other words remove single line comments) 
 *  "(?s)/\*.*?\* /"  with "" (in other words remove comments)
 *               Unwanted extra space to not stop comment
 *               
 * 
 */
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;

import org.apache.commons.io.FileUtils;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.settings.model.CSourceEntry;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICSettingEntry;
import org.eclipse.cdt.core.settings.model.ICSourceEntry;
import org.eclipse.cdt.core.settings.model.util.CDataUtil;
import org.eclipse.cdt.core.settings.model.util.IPathSettingsContainerVisitor;
import org.eclipse.cdt.core.settings.model.util.PathSettingsContainer;
import org.eclipse.cdt.managedbuilder.core.BuildException;
import org.eclipse.cdt.managedbuilder.core.IBuildObject;
import org.eclipse.cdt.managedbuilder.core.IBuilder;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IFileInfo;
import org.eclipse.cdt.managedbuilder.core.IFolderInfo;
import org.eclipse.cdt.managedbuilder.core.IInputType;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.IManagedCommandLineGenerator;
import org.eclipse.cdt.managedbuilder.core.IManagedCommandLineInfo;
import org.eclipse.cdt.managedbuilder.core.IManagedOutputNameProvider;
import org.eclipse.cdt.managedbuilder.core.IOption;
import org.eclipse.cdt.managedbuilder.core.IOutputType;
import org.eclipse.cdt.managedbuilder.core.IResourceInfo;
import org.eclipse.cdt.managedbuilder.core.ITool;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin;
import org.eclipse.cdt.managedbuilder.internal.core.ManagedMakeMessages;
import org.eclipse.cdt.managedbuilder.internal.core.Tool;
import org.eclipse.cdt.managedbuilder.internal.macros.BuildMacroProvider;
import org.eclipse.cdt.managedbuilder.internal.macros.FileContextData;
import org.eclipse.cdt.managedbuilder.macros.BuildMacroException;
import org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider;
import org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator;
import org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator2;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyCalculator;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyCommands;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyGenerator;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyGenerator2;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyGeneratorType;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyInfo;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyPreBuild;
import org.eclipse.cdt.managedbuilder.makegen.gnu.IManagedBuildGnuToolInfo;
import org.eclipse.cdt.utils.EFSExtensionManager;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;

import io.sloeber.core.Messages;
import io.sloeber.core.common.Common;
import io.sloeber.core.common.Const;

/**
 * This is a specialized makefile generator that takes advantage of the
 * extensions present in Gnu Make.
 *
 * @since 1.2
 * @noextend This class is not intended to be subclassed by clients.
 * @noinstantiate This class is not intended to be instantiated by clients.
 */
@SuppressWarnings({ "deprecation", "restriction", "nls", "unused", "synthetic-access", "static-method", "unchecked",
        "hiding" })
public class ArduinoGnuMakefileGenerator implements IManagedBuilderMakefileGenerator2 {
    private static final IPath DOT_SLASH_PATH = new Path("./");
    private static final String FILE_SEPARATOR = File.separator;

    /**
     * This class walks the delta supplied by the build system to determine what
     * resources have been changed. The logic is very simple. If a buildable
     * resource (non-header) has been added or removed, the directories in which
     * they are located are "dirty" so the makefile fragments for them have to be
     * regenerated.
     * <p>
     * The actual dependencies are recalculated as a result of the build step
     * itself. We are relying on make to do the right things when confronted with a
     * dependency on a moved header file. That said, make will treat the missing
     * header file in a dependency rule as a target it has to build unless told
     * otherwise. These dummy targets are added to the makefile to avoid a missing
     * target error.
     */
    public class ResourceDeltaVisitor implements IResourceDeltaVisitor {
        private final ArduinoGnuMakefileGenerator generator;
        private final IConfiguration config;

        /**
         * The constructor
         */
        public ResourceDeltaVisitor(ArduinoGnuMakefileGenerator generator, IManagedBuildInfo info) {
            this.generator = generator;
            this.config = info.getDefaultConfiguration();
        }

        public ResourceDeltaVisitor(ArduinoGnuMakefileGenerator generator, IConfiguration cfg) {
            this.generator = generator;
            this.config = cfg;
        }

        /*
         * (non-Javadoc)
         *
         * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.
         * core.resources.IResourceDelta)
         */
        @Override
        public boolean visit(IResourceDelta delta) throws CoreException {
            // Should the visitor keep iterating in current directory
            boolean keepLooking = false;
            IResource resource = delta.getResource();
            IResourceInfo rcInfo = config.getResourceInfo(resource.getProjectRelativePath(), false);
            IFolderInfo fo = null;
            boolean isSource = isSource(resource.getProjectRelativePath());
            if (rcInfo instanceof IFolderInfo) {
                fo = (IFolderInfo) rcInfo;
            }
            // What kind of resource change has occurred
            if (isSource) {
                if (resource.getType() == IResource.FILE) {
                    String ext = resource.getFileExtension();
                    switch (delta.getKind()) {
                    case IResourceDelta.ADDED:
                        if (!generator.isGeneratedResource(resource)) {
                            // This is a source file so just add its container
                            if (fo == null || fo.buildsFileType(ext)) {
                                generator.appendModifiedSubdirectory(resource);
                            }
                        }
                        break;
                    case IResourceDelta.REMOVED:
                        // we get this notification if a resource is moved too
                        if (!generator.isGeneratedResource(resource)) {
                            // This is a source file so just add its container
                            if (fo == null || fo.buildsFileType(ext)) {
                                generator.appendDeletedFile(resource);
                                generator.appendModifiedSubdirectory(resource);
                            }
                        }
                        break;
                    default:
                        keepLooking = true;
                        break;
                    }
                }
                if (resource.getType() == IResource.FOLDER) {
                    // I only care about delete event
                    switch (delta.getKind()) {
                    case IResourceDelta.REMOVED:
                        if (!generator.isGeneratedResource(resource)) {
                            generator.appendDeletedSubdirectory((IContainer) resource);
                        }
                        break;
                    }
                }
            }
            if (resource.getType() == IResource.PROJECT) {
                // If there is a zero-length delta, something the project
                // depends on has changed so just call make
                IResourceDelta[] children = delta.getAffectedChildren();
                if (children != null && children.length > 0) {
                    keepLooking = true;
                }
            } else {
                // If the resource is part of the generated directory structure
                // don't recurse
                if (resource.getType() == IResource.ROOT || (isSource && !generator.isGeneratedResource(resource))) {
                    keepLooking = true;
                }
            }
            return keepLooking;
        }
    }

    /**
     * This class is used to recursively walk the project and determine which
     * modules contribute buildable source files.
     */
    protected class ResourceProxyVisitor implements IResourceProxyVisitor {
        private final ArduinoGnuMakefileGenerator generator;
        private final IConfiguration config;

        /**
         * Constructs a new resource proxy visitor to quickly visit project resources.
         */
        public ResourceProxyVisitor(ArduinoGnuMakefileGenerator generator, IManagedBuildInfo info) {
            this.generator = generator;
            this.config = info.getDefaultConfiguration();
        }

        public ResourceProxyVisitor(ArduinoGnuMakefileGenerator generator, IConfiguration cfg) {
            this.generator = generator;
            this.config = cfg;
        }

        /*
         * (non-Javadoc)
         *
         * @see org.eclipse.core.resources.IResourceProxyVisitor#visit(org.eclipse.
         * core.resources.IResourceProxy)
         */
        @Override
        public boolean visit(IResourceProxy proxy) throws CoreException {
            // No point in proceeding, is there
            if (generator == null) {
                return false;
            }
            IResource resource = proxy.requestResource();
            boolean isSource = isSource(resource.getProjectRelativePath());
            // Is this a resource we should even consider
            if (proxy.getType() == IResource.FILE) {
                // If this resource has a Resource Configuration and is not
                // excluded or
                // if it has a file extension that one of the tools builds, add
                // the sudirectory to the list
                // boolean willBuild = false;
                IResourceInfo rcInfo = config.getResourceInfo(resource.getProjectRelativePath(), false);
                if (isSource) {
                    boolean willBuild = false;
                    if (rcInfo instanceof IFolderInfo) {
                        String ext = resource.getFileExtension();
                        if (((IFolderInfo) rcInfo).buildsFileType(ext) && !generator.isGeneratedResource(resource)) {
                            willBuild = true;
                        }
                    } else {
                        willBuild = true;
                    }
                    if (willBuild)
                        generator.appendBuildSubdirectory(resource);
                }
                return false;
            } else if (proxy.getType() == IResource.FOLDER) {
                if (!isSource || generator.isGeneratedResource(resource))
                    return false;
                return true;
            }
            // Recurse into subdirectories
            return true;
        }
    }

    // String constants for makefile contents and messages
    private static final String COMMENT = "MakefileGenerator.comment";
    private static final String HEADER = COMMENT + ".header";
    protected static final String MESSAGE_FINISH_BUILD = ManagedMakeMessages
            .getResourceString("MakefileGenerator.message.finish.build");
    protected static final String MESSAGE_FINISH_FILE = ManagedMakeMessages
            .getResourceString("MakefileGenerator.message.finish.file");
    protected static final String MESSAGE_START_BUILD = ManagedMakeMessages
            .getResourceString("MakefileGenerator.message.start.build");
    protected static final String MESSAGE_START_FILE = ManagedMakeMessages
            .getResourceString("MakefileGenerator.message.start.file");
    protected static final String MESSAGE_START_DEPENDENCY = ManagedMakeMessages
            .getResourceString("MakefileGenerator.message.start.dependency");
    protected static final String MESSAGE_NO_TARGET_TOOL = ManagedMakeMessages
            .getResourceString("MakefileGenerator.message.no.target");
    private static final String MOD_LIST = COMMENT + ".module.list";
    private static final String MOD_VARS = COMMENT + ".module.variables";
    private static final String MOD_RULES = COMMENT + ".build.rule";
    private static final String BUILD_TOP = COMMENT + ".build.toprules";
    private static final String ALL_TARGET = COMMENT + ".build.alltarget";
    private static final String MAINBUILD_TARGET = COMMENT + ".build.mainbuildtarget";
    private static final String BUILD_TARGETS = COMMENT + ".build.toptargets";
    private static final String SRC_LISTS = COMMENT + ".source.list";
    private static final String EMPTY_STRING = "";
    private static final String[] EMPTY_STRING_ARRAY = new String[0];
    private static final String OBJS_MACRO = "OBJS";
    private static final String MACRO_ADDITION_ADDPREFIX_HEADER = "${addprefix ";
    private static final String MACRO_ADDITION_ADDPREFIX_SUFFIX = "," + WHITESPACE + LINEBREAK;
    private static final String MACRO_ADDITION_PREFIX_SUFFIX = "+=" + WHITESPACE + LINEBREAK;
    private static final String PREBUILD = "pre-build";
    private static final String MAINBUILD = "main-build";
    private static final String POSTBUILD = "post-build";
    private static final String SECONDARY_OUTPUTS = "secondary-outputs";
    // Enumerations
    public static final int PROJECT_RELATIVE = 1, PROJECT_SUBDIR_RELATIVE = 2, ABSOLUTE = 3;

    class ToolInfoHolder {
        ITool[] buildTools;
        boolean[] buildToolsUsed;
        ArduinoManagedBuildGnuToolInfo[] gnuToolInfos;
        Set<String> outputExtensionsSet;
        List<IPath> dependencyMakefiles;
    }

    // Local variables needed by generator
    private String buildTargetName;
    private String buildTargetExt;
    IConfiguration config;
    private IBuilder builder;
    private PathSettingsContainer toolInfos;
    private Vector<IResource> deletedFileList;
    private Vector<IResource> deletedDirList;
    private Vector<IResource> invalidDirList;
    /** Collection of Folders in which sources files have been modified */
    private Collection<IContainer> modifiedList;
    private IProgressMonitor monitor;
    private IProject project;
    private IResource[] projectResources;
    private Vector<String> ruleList;
    private Vector<String> depLineList;
    // lines
    private Vector<String> depRuleList;
    // dependency files
    /** Collection of Containers which contribute source files to the build */
    private Collection<IContainer> subdirList;
    private IPath topBuildDir;
    final HashMap<String, List<IPath>> buildSrcVars = new HashMap<>();
    final HashMap<String, List<IPath>> buildOutVars = new HashMap<>();
    final HashMap<String, ArduinoGnuDependencyGroupInfo> buildDepVars = new HashMap<>();
    private final LinkedHashMap<String, String> topBuildOutVars = new LinkedHashMap<>();
    // Dependency file variables
    // private Vector dependencyMakefiles; // IPath's - relative to the top
    // build directory or absolute
    private ICSourceEntry srcEntries[];
    // Added by jaba
    private IOutputType usedOutType = null;
    // End of added by jaba

    public ArduinoGnuMakefileGenerator() {
        super();
    }

    /*************************************************************************
     * IManagedBuilderMakefileGenerator M E T H O D S
     ************************************************************************/
    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator
     * #initialize(IProject, IManagedBuildInfo, IProgressMonitor)
     */
    @Override
    public void initialize(IProject project, IManagedBuildInfo info, IProgressMonitor monitor) {
        this.project = project;
        try {
            projectResources = project.members();
        } catch (CoreException e) {
            projectResources = null;
        }
        // Save the monitor reference for reporting back to the user
        this.monitor = monitor;
        // Get the name of the build target
        buildTargetName = info.getBuildArtifactName();
        // Get its extension
        buildTargetExt = info.getBuildArtifactExtension();
        try {
            // try to resolve the build macros in the target extension
            buildTargetExt = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(buildTargetExt,
                    "", " ", IBuildMacroProvider.CONTEXT_CONFIGURATION, info.getDefaultConfiguration());
        } catch (BuildMacroException e) {
            /* JABA is not going to write this code */
        }
        try {
            // try to resolve the build macros in the target name
            String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(buildTargetName,
                    "", " ", IBuildMacroProvider.CONTEXT_CONFIGURATION, info.getDefaultConfiguration());
            if (resolved != null && (resolved = resolved.trim()).length() > 0)
                buildTargetName = resolved;
        } catch (BuildMacroException e) {
            /* JABA is not going to write this code */
        }
        if (buildTargetExt == null) {
            buildTargetExt = "";
        }
        // Cache the build tools
        config = info.getDefaultConfiguration();
        builder = config.getEditableBuilder();
        initToolInfos();
        // set the top build dir path
        topBuildDir = project.getFolder(info.getConfigurationName()).getFullPath();
    }

    /**
     * This method calls the dependency postprocessors defined for the tool chain
     */
    private void callDependencyPostProcessors(IResourceInfo rcInfo, ToolInfoHolder h, IFile depFile,
            IManagedDependencyGenerator2[] postProcessors, boolean callPopulateDummyTargets, boolean force)
            throws CoreException {
        try {
            updateMonitor(ManagedMakeMessages
                    .getFormattedString("ArduinoGnuMakefileGenerator.message.postproc.dep.file", depFile.getName()));
            if (postProcessors != null) {
                IPath absolutePath = new Path(
                        EFSExtensionManager.getDefault().getPathFromURI(depFile.getLocationURI()));
                // Convert to build directory relative
                IPath depPath = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), absolutePath);
                for (int i = 0; i < postProcessors.length; i++) {
                    IManagedDependencyGenerator2 depGen = postProcessors[i];
                    if (depGen != null) {
                        depGen.postProcessDependencyFile(depPath, config, h.buildTools[i], getTopBuildDir());
                    }
                }
            }
            if (callPopulateDummyTargets) {
                populateDummyTargets(rcInfo, depFile, force);
            }
        } catch (CoreException e) {
            throw e;
        } catch (IOException e) {
            /* JABA is not going to write this code */
        }
    }

    /**
     * This method collects the dependency postprocessors and file extensions
     * defined for the tool chain
     */
    private boolean collectDependencyGeneratorInformation(ToolInfoHolder h, Vector<String> depExts,
            IManagedDependencyGenerator2[] postProcessors) {
        boolean callPopulateDummyTargets = false;
        for (int i = 0; i < h.buildTools.length; i++) {
            ITool tool = h.buildTools[i];
            IManagedDependencyGeneratorType depType = tool
                    .getDependencyGeneratorForExtension(tool.getDefaultInputExtension());
            if (depType != null) {
                int calcType = depType.getCalculatorType();
                if (calcType <= IManagedDependencyGeneratorType.TYPE_OLD_TYPE_LIMIT) {
                    if (calcType == IManagedDependencyGeneratorType.TYPE_COMMAND) {
                        callPopulateDummyTargets = true;
                        depExts.add(DEP_EXT);
                    }
                } else {
                    if (calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS
                            || calcType == IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS) {
                        IManagedDependencyGenerator2 depGen = (IManagedDependencyGenerator2) depType;
                        String depExt = depGen.getDependencyFileExtension(config, tool);
                        if (depExt != null) {
                            postProcessors[i] = depGen;
                            depExts.add(depExt);
                        }
                    }
                }
            }
        }
        return callPopulateDummyTargets;
    }

    protected boolean isSource(IPath path) {
        return !CDataUtil.isExcluded(path, srcEntries);
    }

    private class DepInfo {
        Vector<String> depExts;
        IManagedDependencyGenerator2[] postProcessors;
        boolean callPopulateDummyTargets;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator
     * #generateDependencies()
     */
    @Override
    public void generateDependencies() throws CoreException {
        final PathSettingsContainer postProcs = PathSettingsContainer.createRootContainer();
        // Note: PopulateDummyTargets is a hack for the pre-3.x GCC compilers
        // Collect the methods that will need to be called
        toolInfos.accept(new IPathSettingsContainerVisitor() {
            @Override
            public boolean visit(PathSettingsContainer container) {
                ToolInfoHolder h = (ToolInfoHolder) container.getValue();
                Vector<String> depExts = new Vector<>();
                IManagedDependencyGenerator2[] postProcessors = new IManagedDependencyGenerator2[h.buildTools.length];
                boolean callPopulateDummyTargets = collectDependencyGeneratorInformation(h, depExts, postProcessors);
                // Is there anyone to call if we do find dependency files?
                if (!callPopulateDummyTargets) {
                    int i;
                    for (i = 0; i < postProcessors.length; i++) {
                        if (postProcessors[i] != null)
                            break;
                    }
                    if (i == postProcessors.length)
                        return true;
                }
                PathSettingsContainer child = postProcs.getChildContainer(container.getPath(), true, true);
                DepInfo di = new DepInfo();
                di.depExts = depExts;
                di.postProcessors = postProcessors;
                di.callPopulateDummyTargets = callPopulateDummyTargets;
                child.setValue(di);
                return true;
            }
        });
        IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
        for (IResource res : getSubdirList()) {
            // The builder creates a subdir with same name as source in the
            // build location
            IContainer subDir = (IContainer) res;
            IPath projectRelativePath = subDir.getProjectRelativePath();
            IResourceInfo rcInfo = config.getResourceInfo(projectRelativePath, false);
            PathSettingsContainer cr = postProcs.getChildContainer(rcInfo.getPath(), false, true);
            if (cr == null || cr.getValue() == null)
                continue;
            DepInfo di = (DepInfo) cr.getValue();
            ToolInfoHolder h = getToolInfo(projectRelativePath);
            IPath buildRelativePath = topBuildDir.append(projectRelativePath);
            IFolder buildFolder = root.getFolder(buildRelativePath);
            if (buildFolder == null)
                continue;
            if (!buildFolder.exists())
                continue;
            // Find all of the dep files in the generated subdirectories
            IResource[] files = buildFolder.members();
            for (IResource file : files) {
                String fileExt = file.getFileExtension();
                for (String ext : di.depExts) {
                    if (ext.equals(fileExt)) {
                        IFile depFile = root.getFile(file.getFullPath());
                        if (depFile == null)
                            continue;
                        callDependencyPostProcessors(rcInfo, h, depFile, di.postProcessors, di.callPopulateDummyTargets,
                                false);
                    }
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#
     * generateMakefiles(org.eclipse.core.resources.IResourceDelta)
     */
    @Override
    public MultiStatus generateMakefiles(IResourceDelta delta) throws CoreException {
        /*
         * Let's do a sanity check right now. 1. This is an incremental build, so if the
         * top-level directory is not there, then a rebuild is needed.
         */
        IFolder folder = project.getFolder(config.getName());
        if (!folder.exists()) {
            return regenerateMakefiles();
        }
        // Return value
        MultiStatus status;
        // Visit the resources in the delta and compile a list of subdirectories
        // to regenerate
        updateMonitor(
                ManagedMakeMessages.getFormattedString("MakefileGenerator.message.calc.delta", project.getName()));
        ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(this, config);
        delta.accept(visitor);
        checkCancel();
        // Get all the subdirectories participating in the build
        updateMonitor(
                ManagedMakeMessages.getFormattedString("MakefileGenerator.message.finding.sources", project.getName()));
        ResourceProxyVisitor resourceVisitor = new ResourceProxyVisitor(this, config);
        project.accept(resourceVisitor, IResource.NONE);
        checkCancel();
        // Bug 303953: Ensure that if all resources have been removed from a
        // folder, than the folder still
        // appears in the subdir list so it's subdir.mk is correctly regenerated
        getSubdirList().addAll(getModifiedList());
        // Make sure there is something to build
        if (getSubdirList().isEmpty()) {
            String info = ManagedMakeMessages.getFormattedString("MakefileGenerator.warning.no.source",
                    project.getName());
            updateMonitor(info);
            status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.INFO, "", null);
            status.add(new Status(IStatus.INFO, ManagedBuilderCorePlugin.getUniqueIdentifier(), NO_SOURCE_FOLDERS, info,
                    null));
            return status;
        }
        // Make sure the build directory is available
        topBuildDir = createDirectory(config.getName());
        checkCancel();
        // Make sure that there is a makefile containing all the folders
        // participating
        IPath srcsFilePath = topBuildDir.append(SRCSFILE_NAME);
        IFile srcsFileHandle = createFile(srcsFilePath);
        buildSrcVars.clear();
        buildOutVars.clear();
        buildDepVars.clear();
        topBuildOutVars.clear();
        populateSourcesMakefile(srcsFileHandle);
        checkCancel();
        // Regenerate any fragments that are missing for the exisiting
        // directories NOT modified
        for (IResource res : getSubdirList()) {
            IContainer subdirectory = (IContainer) res;
            if (!getModifiedList().contains(subdirectory)) {
                // Make sure the directory exists (it may have been deleted)
                if (!subdirectory.exists()) {
                    appendDeletedSubdirectory(subdirectory);
                    continue;
                }
                // Make sure a fragment makefile exists
                IPath fragmentPath = getBuildWorkingDir().append(subdirectory.getProjectRelativePath())
                        .append(MODFILE_NAME);
                IFile makeFragment = project.getFile(fragmentPath);
                if (!makeFragment.exists()) {
                    // If one or both are missing, then add it to the list to be
                    // generated
                    getModifiedList().add(subdirectory);
                }
            }
        }
        // Delete the old dependency files for any deleted resources
        for (IResource deletedFile : getDeletedFileList()) {
            deleteDepFile(deletedFile);
            deleteBuildTarget(deletedFile);
        }
        // Regenerate any fragments for modified directories
        for (IResource res : getModifiedList()) {
            IContainer subDir = (IContainer) res;
            // Make sure the directory exists (it may have been deleted)
            if (!subDir.exists()) {
                appendDeletedSubdirectory(subDir);
                continue;
            }
            // populateFragmentMakefile(subDir); // See below
            checkCancel();
        }
        // Recreate all module makefiles
        // NOTE WELL: For now, always recreate all of the fragment makefile.
        // This is necessary
        // in order to re-populate the buildVariable lists. In the future, the
        // list could
        // possibly segmented by subdir so that all fragments didn't need to be
        // regenerated
        for (IResource res : getSubdirList()) {
            IContainer subDir = (IContainer) res;
            try {
                populateFragmentMakefile(subDir);
            } catch (CoreException e) {
                // Probably should ask user if they want to continue
                checkCancel();
                continue;
            }
            checkCancel();
        }
        // Calculate the inputs and outputs of the Tools to be generated in the
        // main makefile
        calculateToolInputsOutputs();
        checkCancel();
        // Re-create the top-level makefile
        IPath makefilePath = topBuildDir.append(MAKEFILE_NAME);
        IFile makefileHandle = createFile(makefilePath);
        populateTopMakefile(makefileHandle, false);
        checkCancel();
        // Remove deleted folders from generated build directory
        for (IResource res : getDeletedDirList()) {
            IContainer subDir = (IContainer) res;
            removeGeneratedDirectory(subDir);
            checkCancel();
        }
        // How did we do
        if (!getInvalidDirList().isEmpty()) {
            status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.WARNING, "", null);
            // Add a new status for each of the bad folders
            // TODO: fix error message
            for (IResource res : getInvalidDirList()) {
                IContainer subDir = (IContainer) res;
                status.add(new Status(IStatus.WARNING, ManagedBuilderCorePlugin.getUniqueIdentifier(), SPACES_IN_PATH,
                        subDir.getFullPath().toString(), null));
            }
        } else {
            status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.OK, "", null);
        }
        return status;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#
     * getBuildWorkingDir()
     */
    @Override
    public IPath getBuildWorkingDir() {
        if (topBuildDir != null) {
            return topBuildDir.removeFirstSegments(1);
        }
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#
     * getMakefileName()
     */
    @Override
    public String getMakefileName() {
        return MAKEFILE_NAME;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#
     * isGeneratedResource(org.eclipse.core.resources.IResource)
     */
    @Override
    public boolean isGeneratedResource(IResource resource) {
        // Is this a generated directory ...
        IPath path = resource.getProjectRelativePath();
        // TODO: fix to use builder output dir instead
        String[] configNames = ManagedBuildManager.getBuildInfo(project).getConfigurationNames();
        for (String name : configNames) {
            IPath root = new Path(name);
            // It is if it is a root of the resource pathname
            if (root.isPrefixOf(path))
                return true;
        }
        return false;
    }

    private static void save(StringBuffer buffer, IFile file) throws CoreException {
        String encoding = null;
        try {
            encoding = file.getCharset();
        } catch (CoreException ce) {
            // use no encoding
        }
        byte[] bytes = null;
        if (encoding != null) {
            try {
                bytes = buffer.toString().getBytes(encoding);
            } catch (Exception e) {
                /* JABA is not going to write this code */
            }
        } else {
            bytes = buffer.toString().getBytes();
        }
        ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
        // use a platform operation to update the resource contents
        boolean force = true;
        file.setContents(stream, force, false, null);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#
     * regenerateDependencies()
     */
    @Override
    public void regenerateDependencies(boolean force) throws CoreException {
        // A hack for the pre-3.x GCC compilers is to put dummy targets for deps
        final IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
        final CoreException[] es = new CoreException[1];
        toolInfos.accept(new IPathSettingsContainerVisitor() {
            @Override
            public boolean visit(PathSettingsContainer container) {
                ToolInfoHolder h = (ToolInfoHolder) container.getValue();
                // Collect the methods that will need to be called
                Vector<String> depExts = new Vector<String>();
                IManagedDependencyGenerator2[] postProcessors = new IManagedDependencyGenerator2[h.buildTools.length];
                boolean callPopulateDummyTargets = collectDependencyGeneratorInformation(h, depExts, postProcessors);
                // Is there anyone to call if we do find dependency files?
                if (!callPopulateDummyTargets) {
                    int i;
                    for (i = 0; i < postProcessors.length; i++) {
                        if (postProcessors[i] != null)
                            break;
                    }
                    if (i == postProcessors.length)
                        return true;
                }
                IResourceInfo rcInfo = config.getResourceInfo(container.getPath(), false);
                for (IPath path : getDependencyMakefiles(h)) {
                    // The path to search for the dependency makefile
                    IPath relDepFilePath = topBuildDir.append(path);
                    IFile depFile = root.getFile(relDepFilePath);
                    if (depFile == null || !depFile.isAccessible())
                        continue;
                    try {
                        callDependencyPostProcessors(rcInfo, h, depFile, postProcessors, callPopulateDummyTargets,
                                true);
                    } catch (CoreException e) {
                        es[0] = e;
                        return false;
                    }
                }
                return true;
            }
        });
        if (es[0] != null)
            throw es[0];
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#
     * regenerateMakefiles()
     */
    @Override
    public MultiStatus regenerateMakefiles() throws CoreException {
        MultiStatus status;
        // Visit the resources in the project
        ResourceProxyVisitor visitor = new ResourceProxyVisitor(this, config);
        project.accept(visitor, IResource.NONE);
        // See if the user has cancelled the build
        checkCancel();
        // Populate the makefile if any buildable source files have been found
        // in the project
        if (getSubdirList().isEmpty()) {
            String info = ManagedMakeMessages.getFormattedString("MakefileGenerator.warning.no.source",
                    project.getName());
            updateMonitor(info);
            status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.INFO, "", null);
            status.add(new Status(IStatus.INFO, ManagedBuilderCorePlugin.getUniqueIdentifier(), NO_SOURCE_FOLDERS, info,
                    null));
            return status;
        }
        // Create the top-level directory for the build output
        topBuildDir = createDirectory(config.getName());
        checkCancel();
        // Get the list of subdirectories
        IPath srcsFilePath = topBuildDir.append(SRCSFILE_NAME);
        IFile srcsFileHandle = createFile(srcsFilePath);
        buildSrcVars.clear();
        buildOutVars.clear();
        buildDepVars.clear();
        topBuildOutVars.clear();
        populateSourcesMakefile(srcsFileHandle);
        checkCancel();
        // Now populate the module makefiles
        for (IResource res : getSubdirList()) {
            IContainer subDir = (IContainer) res;
            try {
                populateFragmentMakefile(subDir);
            } catch (CoreException e) {
                // Probably should ask user if they want to continue
                checkCancel();
                continue;
            }
            checkCancel();
        }
        // Calculate the inputs and outputs of the Tools to be generated in the
        // main makefile
        calculateToolInputsOutputs();
        checkCancel();
        // Create the top-level makefile
        IPath makefilePath = topBuildDir.append(MAKEFILE_NAME);
        IFile makefileHandle = createFile(makefilePath);
        populateTopMakefile(makefileHandle, true);
        // JABA SLOEBER create the size.awk file
        ICConfigurationDescription confDesc = ManagedBuildManager.getDescriptionForConfiguration(config);
        IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
        IFile sizeAwkFile1 = root.getFile(topBuildDir.append("size.awk"));
        File sizeAwkFile = sizeAwkFile1.getLocation().toFile();
        String regex = Common.getBuildEnvironmentVariable(confDesc, "recipe.size.regex", EMPTY);
         String awkContent = "/" + regex + "/ {arduino_size += $2 }\n";
        regex = Common.getBuildEnvironmentVariable(confDesc, "recipe.size.regex.data", EMPTY);
        awkContent += "/" + regex + "/ {arduino_data += $2 }\n";
        regex = Common.getBuildEnvironmentVariable(confDesc, "recipe.size.regex.eeprom", EMPTY);
        awkContent += "/" + regex + "/ {arduino_eeprom += $2 }\n";
        awkContent += "END { print \"\\n";
        String max = Common.getBuildEnvironmentVariable(confDesc, "upload.maximum_size", "10000");
        awkContent += Messages.sizeReportSketch.replace("maximum_size", max);
        awkContent += "\\n";
        max = Common.getBuildEnvironmentVariable(confDesc, "upload.maximum_data_size", "10000");
        awkContent += Messages.sizeReportData.replace("maximum_data_size", max);
        awkContent += "\\n";
        awkContent += "\"}";

        try {
            FileUtils.write(sizeAwkFile, awkContent, Charset.defaultCharset());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // END JABA SLOEBER create the size.awk file
        checkCancel();
        // Now finish up by adding all the object files
        IPath objFilePath = topBuildDir.append(OBJECTS_MAKFILE);
        IFile objsFileHandle = createFile(objFilePath);
        populateObjectsMakefile(objsFileHandle);
        checkCancel();
        // How did we do
        if (!getInvalidDirList().isEmpty()) {
            status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.WARNING, "", null);
            // Add a new status for each of the bad folders
            // TODO: fix error message
            for (IResource dir : getInvalidDirList()) {
                status.add(new Status(IStatus.WARNING, ManagedBuilderCorePlugin.getUniqueIdentifier(), SPACES_IN_PATH,
                        dir.getFullPath().toString(), null));
            }
        } else {
            status = new MultiStatus(ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.OK, "", null);
        }
        return status;
    }

    /*************************************************************************
     * M A K E F I L E S P O P U L A T I O N M E T H O D S
     ************************************************************************/
    /**
     * This method generates a "fragment" make file (subdir.mk). One of these is
     * generated for each project directory/subdirectory that contains source files.
     */
    protected void populateFragmentMakefile(IContainer module) throws CoreException {
        // Calculate the new directory relative to the build output
        IPath moduleRelativePath = module.getProjectRelativePath();
        IPath buildRoot = getBuildWorkingDir();
        if (buildRoot == null) {
            return;
        }
        IPath moduleOutputPath = buildRoot.append(moduleRelativePath);
        updateMonitor(ManagedMakeMessages.getFormattedString("MakefileGenerator.message.gen.source.makefile",
                moduleOutputPath.toString()));
        // Now create the directory
        IPath moduleOutputDir = createDirectory(moduleOutputPath.toString());
        // Create a module makefile
        IFile modMakefile = createFile(moduleOutputDir.append(MODFILE_NAME));
        StringBuffer makeBuf = new StringBuffer();
        makeBuf.append(addFragmentMakefileHeader());
        makeBuf.append(addSources(module));
        // Save the files
        save(makeBuf, modMakefile);
    }

    /**
     * The makefile generator generates a Macro for each type of output, other than
     * final artifact, created by the build.
     *
     * @param fileHandle
     *            The file that should be populated with the output
     */
    protected void populateObjectsMakefile(IFile fileHandle) throws CoreException {
        // Master list of "object" dependencies, i.e. dependencies between input
        // files and output files.
        StringBuffer macroBuffer = new StringBuffer();
        List<String> valueList;
        macroBuffer.append(addDefaultHeader());
        // Map of macro names (String) to its definition (List of Strings)
        HashMap<String, List<String>> outputMacros = new HashMap<String, List<String>>();
        // Add the predefined LIBS, USER_OBJS macros
        // Add the libraries this project depends on
        valueList = new ArrayList<String>();
        String[] libs = config.getLibs(buildTargetExt);
        for (String lib : libs) {
            valueList.add(lib);
        }
        outputMacros.put("LIBS", valueList);
        // Add the extra user-specified objects
        valueList = new ArrayList<String>();
        String[] userObjs = config.getUserObjects(buildTargetExt);
        for (String obj : userObjs) {
            valueList.add(obj);
        }
        outputMacros.put("USER_OBJS", valueList);
        // Write every macro to the file
        for (Entry<String, List<String>> entry : outputMacros.entrySet()) {
            macroBuffer.append(entry.getKey()).append(" :=");
            valueList = entry.getValue();
            for (String path : valueList) {
                // These macros will also be used within commands.
                // Make all the slashes go forward so they aren't
                // interpreted as escapes and get lost.
                // See https://bugs.eclipse.org/163672.
                path = path.replace('\\', '/');
                path = ensurePathIsGNUMakeTargetRuleCompatibleSyntax(path);
                macroBuffer.append(WHITESPACE);
                macroBuffer.append(path);
            }
            // terminate the macro definition line
            macroBuffer.append(NEWLINE);
            // leave a blank line before the next macro
            macroBuffer.append(NEWLINE);
        }
        // For now, just save the buffer that was populated when the rules were
        // created
        save(macroBuffer, fileHandle);
    }

    protected void populateSourcesMakefile(IFile fileHandle) throws CoreException {
        // Add the comment
        StringBuffer buffer = addDefaultHeader();
        // Determine the set of macros
        toolInfos.accept(new IPathSettingsContainerVisitor() {
            @Override
            public boolean visit(PathSettingsContainer container) {
                ToolInfoHolder h = (ToolInfoHolder) container.getValue();
                ITool[] buildTools = h.buildTools;
                HashSet<String> handledInputExtensions = new HashSet<String>();
                String buildMacro;
                for (ITool buildTool : buildTools) {
                    if (buildTool.getCustomBuildStep())
                        continue;
                    // Add the known sources macros
                    String[] extensionsList = buildTool.getAllInputExtensions();
                    for (String ext : extensionsList) {
                        // create a macro of the form "EXTENSION_SRCS :="
                        String extensionName = ext;
                        if (!handledInputExtensions.contains(extensionName)) {
                            handledInputExtensions.add(extensionName);
                            buildMacro = getSourceMacroName(extensionName).toString();
                            if (!buildSrcVars.containsKey(buildMacro)) {
                                buildSrcVars.put(buildMacro, new ArrayList<IPath>());
                            }
                            // Add any generated dependency file macros
                            IManagedDependencyGeneratorType depType = buildTool
                                    .getDependencyGeneratorForExtension(extensionName);
                            if (depType != null) {
                                int calcType = depType.getCalculatorType();
                                if (calcType == IManagedDependencyGeneratorType.TYPE_COMMAND
                                        || calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS
                                        || calcType == IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS) {
                                    buildMacro = getDepMacroName(extensionName).toString();
                                    if (!buildDepVars.containsKey(buildMacro)) {
                                        buildDepVars.put(buildMacro, new ArduinoGnuDependencyGroupInfo(buildMacro,
                                                (calcType != IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS)));
                                    }
                                    if (!buildOutVars.containsKey(buildMacro)) {
                                        buildOutVars.put(buildMacro, new ArrayList<IPath>());
                                    }
                                }
                            }
                        }
                    }
                    // Add the specified output build variables
                    IOutputType[] outTypes = buildTool.getOutputTypes();
                    if (outTypes != null && outTypes.length > 0) {
                        for (IOutputType outputType : outTypes) {
                            buildMacro = outputType.getBuildVariable();
                            if (!buildOutVars.containsKey(buildMacro)) {
                                buildOutVars.put(buildMacro, new ArrayList<IPath>());
                            }
                        }
                    } else {
                        // For support of pre-CDT 3.0 integrations.
                        buildMacro = OBJS_MACRO;
                        if (!buildOutVars.containsKey(buildMacro)) {
                            buildOutVars.put(buildMacro, new ArrayList<IPath>());
                        }
                    }
                }
                return true;
            }
        });
        // Add the macros to the makefile
        for (Entry<String, List<IPath>> entry : buildSrcVars.entrySet()) {
            String macroName = new Path(entry.getKey()).toOSString();
            buffer.append(macroName).append(WHITESPACE).append(":=").append(WHITESPACE).append(NEWLINE);
        }
        Set<Entry<String, List<IPath>>> set = buildOutVars.entrySet();
        for (Entry<String, List<IPath>> entry : set) {
            String macroName = new Path(entry.getKey()).toOSString();
            buffer.append(macroName).append(WHITESPACE).append(":=").append(WHITESPACE).append(NEWLINE);
        }
        // Add a list of subdirectories to the makefile
        buffer.append(NEWLINE).append(addSubdirectories());
        // Save the file
        save(buffer, fileHandle);
    }

    /**
     * Create the entire contents of the makefile.
     *
     * @param fileHandle
     *            The file to place the contents in.
     * @param rebuild
     *            FLag signaling that the user is doing a full rebuild
     */
    protected void populateTopMakefile(IFile fileHandle, boolean rebuild) throws CoreException {
        StringBuffer buffer = new StringBuffer();
        // Add the header
        buffer.append(addTopHeader());
        // Add the macro definitions
        buffer.append(addMacros());
        // List to collect needed build output variables
        List<String> outputVarsAdditionsList = new ArrayList<String>();
        // Determine target rules
        StringBuffer targetRules = addTargets(outputVarsAdditionsList, rebuild);
        // Add outputMacros that were added to by the target rules
        buffer.append(writeTopAdditionMacros(outputVarsAdditionsList, getTopBuildOutputVars()));
        // Add target rules
        buffer.append(targetRules);
        // Save the file
        save(buffer, fileHandle);
    }

    /*************************************************************************
     * M A I N (makefile) M A K E F I L E M E T H O D S
     ************************************************************************/
    /**
     * Answers a <code>StringBuffer</code> containing the comment(s) for the
     * top-level makefile.
     */
    protected StringBuffer addTopHeader() {
        return addDefaultHeader();
    }

    /**
     */
    private StringBuffer addMacros() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("-include " + ROOT + FILE_SEPARATOR + MAKEFILE_INIT).append(NEWLINE);
        buffer.append(NEWLINE);
        // Get the clean command from the build model
        buffer.append("RM := ");
        // support macros in the clean command
        String cleanCommand = config.getCleanCommand();
        try {
            cleanCommand = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
                    config.getCleanCommand(), EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_CONFIGURATION,
                    config);
        } catch (BuildMacroException e) {
            // jaba is not going to write this code
        }

        buffer.append(cleanCommand).append(NEWLINE);
        buffer.append(NEWLINE);
        // Now add the source providers
        buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(SRC_LISTS))
                .append(NEWLINE);
        buffer.append("-include sources.mk").append(NEWLINE);
        // JABA Change the include of the "root" (our sketch) folder to be
        // before
        // libraries and other files
        buffer.append("-include subdir.mk").append(NEWLINE);
        // Add includes for each subdir in child-subdir-first order (required
        // for makefile rule matching to work).
        List<String> subDirList = new ArrayList<>();
        for (IContainer subDir : getSubdirList()) {
            String projectRelativePath = subDir.getProjectRelativePath().toOSString();
            if (!projectRelativePath.isEmpty())
                subDirList.add(0, projectRelativePath);
        }
        Collections.sort(subDirList, Collections.reverseOrder());
        for (String dir : subDirList) {
            buffer.append("-include ").append(escapeWhitespaces(dir)).append(FILE_SEPARATOR).append("subdir.mk")
                    .append(NEWLINE);
        }
        // Change the include of the "root" (our sketch) folder to be before
        // libraries and other files
        // buffer.append("-include subdir.mk" + NEWLINE); //
        buffer.append("-include objects.mk").append(NEWLINE).append(NEWLINE);
        // Include generated dependency makefiles if non-empty AND a "clean" has
        // not been requested
        if (!buildDepVars.isEmpty()) {
            buffer.append("ifneq ($(MAKECMDGOALS),clean)").append(NEWLINE);
            for (Entry<String, ArduinoGnuDependencyGroupInfo> entry : buildDepVars.entrySet()) {
                String depsMacro = entry.getKey();
                ArduinoGnuDependencyGroupInfo info = entry.getValue();
                buffer.append("ifneq ($(strip $(").append(depsMacro).append(")),)").append(NEWLINE);
                if (info.conditionallyInclude) {
                    buffer.append("-include $(").append(depsMacro).append(')').append(NEWLINE);
                } else {
                    buffer.append("include $(").append(depsMacro).append(')').append(NEWLINE);
                }
                buffer.append("endif").append(NEWLINE);
            }
            buffer.append("endif").append(NEWLINE).append(NEWLINE);
        }
        // Include makefile.defs supplemental makefile
        buffer.append("-include ").append(ROOT).append(FILE_SEPARATOR).append(MAKEFILE_DEFS).append(NEWLINE);
        return (buffer.append(NEWLINE));
    }

    private StringBuffer addTargets(List<String> outputVarsAdditionsList, boolean rebuild) {
        StringBuffer buffer = new StringBuffer();
        // IConfiguration config = info.getDefaultConfiguration();
        // Assemble the information needed to generate the targets
        String prebuildStep = config.getPrebuildStep();
        // JABA issue927 adding recipe.hooks.sketch.prebuild.NUMBER.pattern as cdt
        // prebuild command if needed
        ICConfigurationDescription confDesc = ManagedBuildManager.getDescriptionForConfiguration(config);
        String sketchPrebuild = io.sloeber.core.common.Common.getBuildEnvironmentVariable(confDesc, "sloeber.prebuild",
                new String(), false);
        if (!sketchPrebuild.isEmpty()) {
            String separator = new String();
            if (!prebuildStep.isEmpty()) {
                prebuildStep = prebuildStep + "\n\t" + sketchPrebuild;
            } else {
                prebuildStep = sketchPrebuild;
            }
        }
        // end off JABA issue927
        try {
            // try to resolve the build macros in the prebuild step
            prebuildStep = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(prebuildStep,
                    EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
        } catch (BuildMacroException e) {
            /* JABA is not going to write this code */
        }
        prebuildStep = prebuildStep.trim();
        String postbuildStep = config.getPostbuildStep();
        try {
            // try to resolve the build macros in the postbuild step
            postbuildStep = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(postbuildStep,
                    EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
        } catch (BuildMacroException e) {
            /* JABA is not going to write this code */
        }
        postbuildStep = postbuildStep.trim();
        String preannouncebuildStep = config.getPreannouncebuildStep();
        String postannouncebuildStep = config.getPostannouncebuildStep();
        String targets = rebuild ? "clean all" : "all";
        ITool targetTool = config.calculateTargetTool();
        // if (targetTool == null) {
        // targetTool = info.getToolFromOutputExtension(buildTargetExt);
        // }
        // Get all the projects the build target depends on
        // If this configuration produces a static archive, building the archive
        // doesn't depend on the output
        // from any of the referenced configurations
        IConfiguration[] refConfigs = new IConfiguration[0];
        if (config.getBuildArtefactType() == null || !ManagedBuildManager.BUILD_ARTEFACT_TYPE_PROPERTY_STATICLIB
                .equals(config.getBuildArtefactType().getId()))
            refConfigs = ManagedBuildManager.getReferencedConfigurations(config);
        /*
         * try { refdProjects = project.getReferencedProjects(); } catch (CoreException
         * e) { // There are 2 exceptions; the project does not exist or it is not open
         * // and neither conditions apply if we are building for it .... }
         */
        // If a prebuild step exists, redefine the all target to be
        // all: {pre-build} main-build
        // and then reset the "traditional" all target to main-build
        // This will allow something meaningful to happen if the generated
        // makefile is
        // extracted and run standalone via "make all"
        //

        // JABA add the arduino upload/program targets
        buffer.append("\n#bootloaderTest\n" + "BurnBootLoader: \n"
                + "\t@echo trying to burn bootloader ${bootloader.tool}\n"
                + "\t${tools.${bootloader.tool}.erase.pattern}\n" + "\t${tools.${bootloader.tool}.bootloader.pattern}\n"
                + "\n" + "uploadWithBuild: all\n"
                + "\t@echo trying to build and upload with upload tool ${upload.tool}\n"
                + "\t${tools.${upload.tool}.upload.pattern}\n" + "\n" + "uploadWithoutBuild: \n"
                + "\t@echo trying to upload without build with upload tool ${upload.tool}\n"
                + "\t${tools.${upload.tool}.upload.pattern}\n" + "    \n" + "uploadWithProgrammerWithBuild: all\n"
                + "\t@echo trying to build and upload with programmer ${program.tool}\n"
                + "\t${tools.${program.tool}.program.pattern}\n" + "\n" + "uploadWithProgrammerWithoutBuild: \n"
                + "\t@echo trying to upload with programmer ${program.tool} without build\n"
                + "\t${tools.${program.tool}.program.pattern}\n\n");
        String defaultTarget = "all:";
        if (prebuildStep.length() > 0) {
            // Add the comment for the "All" target
            buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(ALL_TARGET))
                    .append(NEWLINE);
            buffer.append(defaultTarget).append(NEWLINE);
            buffer.append(TAB).append(MAKE).append(WHITESPACE).append(NO_PRINT_DIR).append(WHITESPACE).append(PREBUILD)
                    .append(NEWLINE);
            buffer.append(TAB).append(MAKE).append(WHITESPACE).append(NO_PRINT_DIR).append(WHITESPACE).append(MAINBUILD)
                    .append(NEWLINE);
            buffer.append(NEWLINE);
            defaultTarget = MAINBUILD.concat(COLON);
            buffer.append(COMMENT_SYMBOL).append(WHITESPACE)
                    .append(ManagedMakeMessages.getResourceString(MAINBUILD_TARGET)).append(NEWLINE);
        } else
            // Add the comment for the "All" target
            buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(ALL_TARGET))
                    .append(NEWLINE);
        // Write out the all target first in case someone just runs make
        // all: <target_name> or mainbuild: <target_name>
        String outputPrefix = EMPTY_STRING;
        if (targetTool != null) {
            outputPrefix = targetTool.getOutputPrefix();
        }
        buffer.append(defaultTarget).append(WHITESPACE).append(outputPrefix)
                .append(ensurePathIsGNUMakeTargetRuleCompatibleSyntax(buildTargetName));
        if (buildTargetExt.length() > 0) {
            buffer.append(DOT).append(buildTargetExt);
        }
        // Add the Secondary Outputs to the all target, if any
        IOutputType[] secondaryOutputs = config.getToolChain().getSecondaryOutputs();
        if (secondaryOutputs.length > 0) {
            buffer.append(WHITESPACE).append(SECONDARY_OUTPUTS);
        }
        buffer.append(NEWLINE).append(NEWLINE);
        /*
         * The build target may depend on other projects in the workspace. These are
         * captured in the deps target: deps: <cd <Proj_Dep_1/build_dir>; $(MAKE) [clean
         * all | all]>
         */
        // Vector managedProjectOutputs = new Vector(refdProjects.length);
        // if (refdProjects.length > 0) {
        Vector<String> managedProjectOutputs = new Vector<String>(refConfigs.length);
        if (refConfigs.length > 0) {
            boolean addDeps = true;
            // if (refdProjects != null) {
            for (IConfiguration depCfg : refConfigs) {
                // IProject dep = refdProjects[i];
                if (!depCfg.isManagedBuildOn())
                    continue;
                // if (!dep.exists()) continue;
                if (addDeps) {
                    buffer.append("dependents:").append(NEWLINE);
                    addDeps = false;
                }
                String buildDir = depCfg.getOwner().getLocation().toOSString();
                String depTargets = targets;
                // if (ManagedBuildManager.manages(dep)) {
                // Add the current configuration to the makefile path
                // IManagedBuildInfo depInfo =
                // ManagedBuildManager.getBuildInfo(dep);
                buildDir += FILE_SEPARATOR + depCfg.getName();
                // Extract the build artifact to add to the dependency list
                String depTarget = depCfg.getArtifactName();
                String depExt = depCfg.getArtifactExtension();
                try {
                    // try to resolve the build macros in the artifact extension
                    depExt = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(depExt, "", " ",
                            IBuildMacroProvider.CONTEXT_CONFIGURATION, depCfg);
                } catch (BuildMacroException e) {
                    /* JABA is not going to write this code */
                }
                try {
                    // try to resolve the build macros in the artifact name
                    String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
                            depTarget, "", " ", IBuildMacroProvider.CONTEXT_CONFIGURATION, depCfg);
                    if ((resolved = resolved.trim()).length() > 0)
                        depTarget = resolved;
                } catch (BuildMacroException e) {
                    /* JABA is not going to write this code */
                }
                String depPrefix = depCfg.getOutputPrefix(depExt);
                if (depCfg.needsRebuild()) {
                    depTargets = "clean all";
                }
                String dependency = buildDir + FILE_SEPARATOR + depPrefix + depTarget;
                if (depExt.length() > 0) {
                    dependency += DOT + depExt;
                }
                dependency = escapeWhitespaces(dependency);
                managedProjectOutputs.add(dependency);
                // }
                buffer.append(TAB).append("-cd").append(WHITESPACE).append(escapeWhitespaces(buildDir))
                        .append(WHITESPACE).append(LOGICAL_AND).append(WHITESPACE).append("$(MAKE) ").append(depTargets)
                        .append(NEWLINE);
            }
            // }
            buffer.append(NEWLINE);
        }
        // Add the targets tool rules
        buffer.append(addTargetsRules(targetTool, outputVarsAdditionsList, managedProjectOutputs,
                (postbuildStep.length() > 0)));
        // Add the prebuild step target, if specified
        if (prebuildStep.length() > 0) {
            buffer.append(PREBUILD).append(COLON).append(NEWLINE);
            if (preannouncebuildStep.length() > 0) {
                buffer.append(TAB).append(DASH).append(AT).append(escapedEcho(preannouncebuildStep));
            }
            buffer.append(TAB).append(DASH).append(prebuildStep).append(NEWLINE);
            buffer.append(TAB).append(DASH).append(AT).append(ECHO_BLANK_LINE).append(NEWLINE);
        }
        // Add the postbuild step, if specified
        if (postbuildStep.length() > 0) {
            buffer.append(POSTBUILD).append(COLON).append(NEWLINE);
            if (postannouncebuildStep.length() > 0) {
                buffer.append(TAB).append(DASH).append(AT).append(escapedEcho(postannouncebuildStep));
            }
            buffer.append(TAB).append(DASH).append(postbuildStep).append(NEWLINE);
            buffer.append(TAB).append(DASH).append(AT).append(ECHO_BLANK_LINE).append(NEWLINE);
        }
        // Add the Secondary Outputs target, if needed
        if (secondaryOutputs.length > 0) {
            buffer.append(SECONDARY_OUTPUTS).append(COLON);
            Vector<String> outs2 = calculateSecondaryOutputs(secondaryOutputs);
            for (int i = 0; i < outs2.size(); i++) {
                buffer.append(WHITESPACE).append("$(").append(outs2.get(i)).append(')');
            }
            buffer.append(NEWLINE).append(NEWLINE);
        }
        // Add all the needed dummy and phony targets
        buffer.append(".PHONY: all clean dependents");
        if (prebuildStep.length() > 0) {
            buffer.append(WHITESPACE).append(MAINBUILD).append(WHITESPACE).append(PREBUILD);
        }
        if (postbuildStep.length() > 0) {
            buffer.append(WHITESPACE).append(POSTBUILD);
        }
        buffer.append(NEWLINE);
        for (String output : managedProjectOutputs) {
            buffer.append(output).append(COLON).append(NEWLINE);
        }
        buffer.append(NEWLINE);
        // Include makefile.targets supplemental makefile
        buffer.append("-include ").append(ROOT).append(FILE_SEPARATOR).append(MAKEFILE_TARGETS).append(NEWLINE);
        return buffer;
    }

    /**
     * Returns the targets rules. The targets make file (top makefile) contains: 1
     * the rule for the final target tool 2 the rules for all of the tools that use
     * multipleOfType in their primary input type 3 the rules for all tools that use
     * the output of #2 tools
     *
     * @param outputVarsAdditionsList
     *            list to add needed build output variables to
     * @param managedProjectOutputs
     *            Other projects in the workspace that this project depends upon
     * @return StringBuffer
     */
    private StringBuffer addTargetsRules(ITool targetTool, List<String> outputVarsAdditionsList,
            Vector<String> managedProjectOutputs, boolean postbuildStep) {
        StringBuffer buffer = new StringBuffer();
        // Add the comment
        buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(BUILD_TOP))
                .append(NEWLINE);
        ToolInfoHolder h = (ToolInfoHolder) toolInfos.getValue();
        ITool[] buildTools = h.buildTools;
        boolean[] buildToolsUsed = h.buildToolsUsed;
        // Get the target tool and generate the rule
        if (targetTool != null) {
            // Note that the name of the target we pass to addRuleForTool does
            // not
            // appear to be used there (and tool outputs are consulted
            // directly), but
            // we quote it anyway just in case it starts to use it in future.
            if (addRuleForTool(targetTool, buffer, true, ensurePathIsGNUMakeTargetRuleCompatibleSyntax(buildTargetName),
                    buildTargetExt, outputVarsAdditionsList, managedProjectOutputs, postbuildStep)) {
                // Mark the target tool as processed
                for (int i = 0; i < buildTools.length; i++) {
                    if (targetTool == buildTools[i]) {
                        buildToolsUsed[i] = true;
                    }
                }
            }
        } else {
            buffer.append(TAB).append(AT).append(escapedEcho(MESSAGE_NO_TARGET_TOOL + WHITESPACE + OUT_MACRO));
        }
        // Generate the rules for all Tools that specify
        // InputType.multipleOfType, and any Tools that
        // consume the output of those tools. This does not apply to pre-3.0
        // integrations, since
        // the only "multipleOfType" tool is the "target" tool
        for (int i = 0; i < buildTools.length; i++) {
            ITool tool = buildTools[i];
            IInputType type = tool.getPrimaryInputType();
            if (type != null && type.getMultipleOfType()) {
                if (!buildToolsUsed[i]) {
                    addRuleForTool(tool, buffer, false, null, null, outputVarsAdditionsList, null, false);
                    // Mark the target tool as processed
                    buildToolsUsed[i] = true;
                    // Look for tools that consume the output
                    generateRulesForConsumers(tool, outputVarsAdditionsList, buffer);
                }
            }
        }
        // Add the comment
        buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(BUILD_TARGETS))
                .append(NEWLINE);
        // Always add a clean target
        buffer.append("clean:").append(NEWLINE);
        buffer.append(TAB).append("-$(RM)").append(WHITESPACE);
        for (Entry<String, List<IPath>> entry : buildOutVars.entrySet()) {
            String macroName = entry.getKey();
            buffer.append("$(").append(macroName).append(')');
        }
        String outputPrefix = EMPTY_STRING;
        if (targetTool != null) {
            outputPrefix = targetTool.getOutputPrefix();
        }
        String completeBuildTargetName = outputPrefix + buildTargetName;
        if (buildTargetExt.length() > 0) {
            completeBuildTargetName = completeBuildTargetName + DOT + buildTargetExt;
        }
        // if (completeBuildTargetName.contains(" ")) {
        // buffer.append(WHITESPACE + "\"" + completeBuildTargetName + "\"");
        // } else {
        // buffer.append(WHITESPACE + completeBuildTargetName);
        // }
        buffer.append(NEWLINE);
        buffer.append(TAB).append(DASH).append(AT).append(ECHO_BLANK_LINE).append(NEWLINE);
        return buffer;
    }

    /**
     * Create the rule
     *
     * @param buffer
     *            Buffer to add makefile rules to
     * @param bTargetTool
     *            True if this is the target tool
     * @param targetName
     *            If this is the "targetTool", the target file name, else
     *            <code>null</code>
     * @param targetExt
     *            If this is the "targetTool", the target file extension, else
     *            <code>null</code>
     * @param outputVarsAdditionsList
     *            list to add needed build output variables to
     * @param managedProjectOutputs
     *            Other projects in the workspace that this project depends upon
     * @param bEmitPostBuildStepCall
     *            Emit post-build step invocation
     */
    protected boolean addRuleForTool(ITool tool, StringBuffer buffer, boolean bTargetTool, String targetName,
            String targetExt, List<String> outputVarsAdditionsList, Vector<String> managedProjectOutputs,
            boolean bEmitPostBuildStepCall) {
        Vector<String> inputs = new Vector<String>();
        Vector<String> dependencies = new Vector<String>();
        Vector<String> outputs = new Vector<String>();
        Vector<String> enumeratedPrimaryOutputs = new Vector<String>();
        Vector<String> enumeratedSecondaryOutputs = new Vector<String>();
        Vector<String> outputVariables = new Vector<String>();
        Vector<String> additionalTargets = new Vector<String>();
        String outputPrefix = EMPTY_STRING;
        if (!getToolInputsOutputs(tool, inputs, dependencies, outputs, enumeratedPrimaryOutputs,
                enumeratedSecondaryOutputs, outputVariables, additionalTargets, bTargetTool, managedProjectOutputs)) {
            return false;
        }
        // If we have no primary output, make all of the secondary outputs the
        // primary output
        if (enumeratedPrimaryOutputs.size() == 0) {
            enumeratedPrimaryOutputs = enumeratedSecondaryOutputs;
            enumeratedSecondaryOutputs.clear();
        }
        // Add the output variables for this tool to our list
        outputVarsAdditionsList.addAll(outputVariables);
        // Create the build rule
        String buildRule = EMPTY_STRING;
        String outflag = tool.getOutputFlag();
        String primaryOutputs = EMPTY_STRING;
        String primaryOutputsQuoted = EMPTY_STRING;
        boolean first = true;
        for (int i = 0; i < enumeratedPrimaryOutputs.size(); i++) {
            String output = enumeratedPrimaryOutputs.get(i);
            if (!first) {
                primaryOutputs += WHITESPACE;
                primaryOutputsQuoted += WHITESPACE;
            }
            first = false;
            primaryOutputs += new Path(output).toOSString();
            primaryOutputsQuoted += ensurePathIsGNUMakeTargetRuleCompatibleSyntax(new Path(output).toOSString());
        }
        buildRule += (primaryOutputsQuoted + COLON + WHITESPACE);
        first = true;
        String calculatedDependencies = EMPTY_STRING;
        for (int i = 0; i < dependencies.size(); i++) {
            String input = dependencies.get(i);
            if (!first)
                calculatedDependencies += WHITESPACE;
            first = false;
            calculatedDependencies += input;
        }
        buildRule += calculatedDependencies;
        // We can't have duplicates in a makefile
        if (getRuleList().contains(buildRule)) {
            /* JABA is not going to write this code */
        } else {
            getRuleList().add(buildRule);
            buffer.append(buildRule).append(NEWLINE);
            if (bTargetTool) {
                buffer.append(TAB).append(AT).append(escapedEcho(MESSAGE_START_BUILD + WHITESPACE + OUT_MACRO));
            }
            buffer.append(TAB).append(AT).append(escapedEcho(tool.getAnnouncement()));
            // Get the command line for this tool invocation
            String[] flags;
            try {
                flags = tool.getToolCommandFlags(null, null);
            } catch (BuildException ex) {
                // TODO report error
                flags = EMPTY_STRING_ARRAY;
            }
            String command = tool.getToolCommand();
            try {
                // try to resolve the build macros in the tool command
                String resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
                        command, EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
                        new FileContextData(null, null, null, tool));
                if ((resolvedCommand = resolvedCommand.trim()).length() > 0) {
                    command = resolvedCommand.replace(" \"\" ", " ");
                }
            } catch (BuildMacroException e) {
                /* JABA is not going to write this code */
            }
            String[] cmdInputs = inputs.toArray(new String[inputs.size()]);
            IManagedCommandLineGenerator gen = tool.getCommandLineGenerator();
            IManagedCommandLineInfo cmdLInfo = gen.generateCommandLineInfo(tool, command, flags, outflag, outputPrefix,
                    primaryOutputs, cmdInputs, tool.getCommandLinePattern());
            // The command to build
            String buildCmd = null;
            if (cmdLInfo == null) {
                String toolFlags;
                try {
                    toolFlags = tool.getToolCommandFlagsString(null, null);
                } catch (BuildException ex) {
                    // TODO report error
                    toolFlags = EMPTY_STRING;
                }
                buildCmd = command + WHITESPACE + toolFlags + WHITESPACE + outflag + WHITESPACE + outputPrefix
                        + primaryOutputs + WHITESPACE + IN_MACRO;
            } else
                buildCmd = cmdLInfo.getCommandLine();
            // resolve any remaining macros in the command after it has been
            // generated
            try {
                String resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
                        buildCmd, EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
                        new FileContextData(null, null, null, tool));
                if ((resolvedCommand = resolvedCommand.trim()).length() > 0) {
                    buildCmd = resolvedCommand.replace(" \"\" ", " ");
                }
            } catch (BuildMacroException e) {
                /* JABA is not going to write this code */
            }
            // buffer.append(TAB).append(AT).append(escapedEcho(buildCmd));
            // buffer.append(TAB).append(AT).append(buildCmd);
            buffer.append(TAB).append(buildCmd);
            // TODO
            // NOTE WELL: Dependency file generation is not handled for this
            // type of Tool
            // Echo finished message
            buffer.append(NEWLINE);
            buffer.append(TAB).append(AT).append(
                    escapedEcho((bTargetTool ? MESSAGE_FINISH_BUILD : MESSAGE_FINISH_FILE) + WHITESPACE + OUT_MACRO));
            buffer.append(TAB).append(AT).append(ECHO_BLANK_LINE);
            // If there is a post build step, then add a recursive invocation of
            // MAKE to invoke it after the main build
            // Note that $(MAKE) will instantiate in the recusive invocation to
            // the make command that was used to invoke
            // the makefile originally
            if (bEmitPostBuildStepCall) {
                buffer.append(TAB).append(MAKE).append(WHITESPACE).append(NO_PRINT_DIR).append(WHITESPACE)
                        .append(POSTBUILD).append(NEWLINE).append(NEWLINE);
            } else {
                // Just emit a blank line
                buffer.append(NEWLINE);
            }
        }
        // If we have secondary outputs, output dependency rules without
        // commands
        if (enumeratedSecondaryOutputs.size() > 0 || additionalTargets.size() > 0) {
            String primaryOutput = enumeratedPrimaryOutputs.get(0);
            Vector<String> addlOutputs = new Vector<String>();
            addlOutputs.addAll(enumeratedSecondaryOutputs);
            addlOutputs.addAll(additionalTargets);
            for (int i = 0; i < addlOutputs.size(); i++) {
                String output = addlOutputs.get(i);
                String depLine = output + COLON + WHITESPACE + primaryOutput + WHITESPACE + calculatedDependencies
                        + NEWLINE;
                if (!getDepLineList().contains(depLine)) {
                    getDepLineList().add(depLine);
                    buffer.append(depLine);
                }
            }
            buffer.append(NEWLINE);
        }
        return true;
    }

    /**
     * @param outputVarsAdditionsList
     *            list to add needed build output variables to
     * @param buffer
     *            buffer to add rules to
     */
    private void generateRulesForConsumers(ITool generatingTool, List<String> outputVarsAdditionsList,
            StringBuffer buffer) {
        // Generate a build rule for any tool that consumes the output of this
        // tool
        ToolInfoHolder h = (ToolInfoHolder) toolInfos.getValue();
        ITool[] buildTools = h.buildTools;
        boolean[] buildToolsUsed = h.buildToolsUsed;
        IOutputType[] outTypes = generatingTool.getOutputTypes();
        for (IOutputType outType : outTypes) {
            String[] outExts = outType.getOutputExtensions(generatingTool);
            String outVariable = outType.getBuildVariable();
            if (outExts != null) {
                for (String outExt : outExts) {
                    for (int k = 0; k < buildTools.length; k++) {
                        ITool tool = buildTools[k];
                        if (!buildToolsUsed[k]) {
                            // Also has to match build variables if specified
                            IInputType inType = tool.getInputType(outExt);
                            if (inType != null) {
                                String inVariable = inType.getBuildVariable();
                                if ((outVariable == null && inVariable == null) || (outVariable != null
                                        && inVariable != null && outVariable.equals(inVariable))) {
                                    if (addRuleForTool(buildTools[k], buffer, false, null, null,
                                            outputVarsAdditionsList, null, false)) {
                                        buildToolsUsed[k] = true;
                                        // Look for tools that consume the
                                        // output
                                        generateRulesForConsumers(buildTools[k], outputVarsAdditionsList, buffer);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    protected boolean getToolInputsOutputs(ITool tool, Vector<String> inputs, Vector<String> dependencies,
            Vector<String> outputs, Vector<String> enumeratedPrimaryOutputs, Vector<String> enumeratedSecondaryOutputs,
            Vector<String> outputVariables, Vector<String> additionalTargets, boolean bTargetTool,
            Vector<String> managedProjectOutputs) {
        ToolInfoHolder h = (ToolInfoHolder) toolInfos.getValue();
        ITool[] buildTools = h.buildTools;
        ArduinoManagedBuildGnuToolInfo[] gnuToolInfos = h.gnuToolInfos;
        // Get the information regarding the tool's inputs and outputs from the
        // objects
        // created by calculateToolInputsOutputs
        IManagedBuildGnuToolInfo toolInfo = null;
        for (int i = 0; i < buildTools.length; i++) {
            if (tool == buildTools[i]) {
                toolInfo = gnuToolInfos[i];
                break;
            }
        }
        if (toolInfo == null)
            return false;
        // Populate the output Vectors
        inputs.addAll(toolInfo.getCommandInputs());
        outputs.addAll(toolInfo.getCommandOutputs());
        enumeratedPrimaryOutputs.addAll(toolInfo.getEnumeratedPrimaryOutputs());
        enumeratedSecondaryOutputs.addAll(toolInfo.getEnumeratedSecondaryOutputs());
        outputVariables.addAll(toolInfo.getOutputVariables());
        Vector<String> unprocessedDependencies = toolInfo.getCommandDependencies();
        for (String path : unprocessedDependencies) {
            dependencies.add(ensurePathIsGNUMakeTargetRuleCompatibleSyntax(path));
        }
        additionalTargets.addAll(toolInfo.getAdditionalTargets());
        if (bTargetTool && managedProjectOutputs != null) {
            for (String output : managedProjectOutputs) {
                dependencies.add(output);
            }
        }
        return true;
    }

    protected Vector<String> calculateSecondaryOutputs(IOutputType[] secondaryOutputs) {
        ToolInfoHolder h = (ToolInfoHolder) toolInfos.getValue();
        ITool[] buildTools = h.buildTools;
        Vector<String> buildVars = new Vector<String>();
        for (int i = 0; i < buildTools.length; i++) {
            // Add the specified output build variables
            IOutputType[] outTypes = buildTools[i].getOutputTypes();
            if (outTypes != null && outTypes.length > 0) {
                for (int j = 0; j < outTypes.length; j++) {
                    IOutputType outType = outTypes[j];
                    // Is this one of the secondary outputs?
                    // Look for an outputType with this ID, or one with a
                    // superclass with this id
                    thisType: for (int k = 0; k < secondaryOutputs.length; k++) {
                        IOutputType matchType = outType;
                        do {
                            if (matchType.getId().equals(secondaryOutputs[k].getId())) {
                                buildVars.add(outType.getBuildVariable());
                                break thisType;
                            }
                            matchType = matchType.getSuperClass();
                        } while (matchType != null);
                    }
                }
            }
        }
        return buildVars;
    }

    protected boolean isSecondaryOutputVar(ToolInfoHolder h, IOutputType[] secondaryOutputs, String varName) {
        ITool[] buildTools = h.buildTools;
        for (ITool buildTool : buildTools) {
            // Add the specified output build variables
            IOutputType[] outTypes = buildTool.getOutputTypes();
            if (outTypes != null && outTypes.length > 0) {
                for (IOutputType outType : outTypes) {
                    // Is this one of the secondary outputs?
                    // Look for an outputType with this ID, or one with a
                    // superclass with this id
                    for (IOutputType secondaryOutput : secondaryOutputs) {
                        IOutputType matchType = outType;
                        do {
                            if (matchType.getId().equals(secondaryOutput.getId())) {
                                if (outType.getBuildVariable().equals(varName)) {
                                    return true;
                                }
                            }
                            matchType = matchType.getSuperClass();
                        } while (matchType != null);
                    }
                }
            }
        }
        return false;
    }

    /*************************************************************************
     * S O U R C E S (sources.mk) M A K E F I L E M E T H O D S
     ************************************************************************/
    private StringBuffer addSubdirectories() {
        StringBuffer buffer = new StringBuffer();
        // Add the comment
        buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(MOD_LIST))
                .append(NEWLINE);
        buffer.append("SUBDIRS := ").append(LINEBREAK);
        // Get all the module names
        for (IResource container : getSubdirList()) {
            updateMonitor(ManagedMakeMessages.getFormattedString("MakefileGenerator.message.adding.source.folder",
                    container.getFullPath().toOSString()));
            // Check the special case where the module is the project root
            if (container.getFullPath() == project.getFullPath()) {
                buffer.append(DOT).append(WHITESPACE).append(LINEBREAK);
            } else {
                IPath path = container.getProjectRelativePath();
                buffer.append(escapeWhitespaces(path.toOSString())).append(WHITESPACE).append(LINEBREAK);
            }
        }
        buffer.append(NEWLINE);
        return buffer;
    }

    /*************************************************************************
     * F R A G M E N T (subdir.mk) M A K E F I L E M E T H O D S
     ************************************************************************/
    /**
     * Returns a <code>StringBuffer</code> containing the comment(s) for a fragment
     * makefile (subdir.mk).
     */

    protected StringBuffer addFragmentMakefileHeader() {
        return addDefaultHeader();
    }

    /**
     * Returns a <code>StringBuffer</code> containing makefile text for all of the
     * sources contributed by a container (project directory/subdirectory) to the
     * fragement makefile
     *
     * @param module
     *            project resource directory/subdirectory
     * @return StringBuffer generated text for the fragement makefile
     */
    protected StringBuffer addSources(IContainer module) throws CoreException {
        // Calculate the new directory relative to the build output
        IPath moduleRelativePath = module.getProjectRelativePath();
        String relativePath = moduleRelativePath.toOSString();
        relativePath += relativePath.length() == 0 ? "" : FILE_SEPARATOR;
        // For build macros in the configuration, create a map which will map
        // them
        // to a string which holds its list of sources.
        LinkedHashMap<String, String> buildVarToRuleStringMap = new LinkedHashMap<String, String>();
        // Add statements that add the source files in this folder,
        // and generated source files, and generated dependency files
        // to the build macros
        for (Entry<String, List<IPath>> entry : buildSrcVars.entrySet()) {
            String macroName = entry.getKey();
            addMacroAdditionPrefix(buildVarToRuleStringMap, macroName, null, false);
        }
        for (Entry<String, List<IPath>> entry : buildOutVars.entrySet()) {
            String macroName = entry.getKey();
            addMacroAdditionPrefix(buildVarToRuleStringMap, macroName, DOT_SLASH_PATH.append(relativePath).toOSString(),
                    false);
        }
        // String buffers
        StringBuffer buffer = new StringBuffer();
        StringBuffer ruleBuffer = new StringBuffer(
                COMMENT_SYMBOL + WHITESPACE + ManagedMakeMessages.getResourceString(MOD_RULES) + NEWLINE);
        // Visit the resources in this folder and add each one to a sources
        // macro, and generate a build rule, if appropriate
        IResource[] resources = module.members();
        IResourceInfo rcInfo;
        IFolder folder = project.getFolder(config.getName());
        for (IResource resource : resources) {
            if (resource.getType() == IResource.FILE) {
                // Check whether this resource is excluded from build
                IPath rcProjRelPath = resource.getProjectRelativePath();
                if (!isSource(rcProjRelPath))
                    continue;
                rcInfo = config.getResourceInfo(rcProjRelPath, false);
                // if( (rcInfo.isExcluded()) )
                // continue;
                addFragmentMakefileEntriesForSource(buildVarToRuleStringMap, ruleBuffer, folder, relativePath, resource,
                        getPathForResource(resource), rcInfo, null, false);
            }
        }
        // Write out the macro addition entries to the buffer
        buffer.append(writeAdditionMacros(buildVarToRuleStringMap));
        return buffer.append(ruleBuffer).append(NEWLINE);
    }

    /*
     * (non-Javadoc Adds the entries for a particular source file to the fragment
     * makefile
     *
     * @param buildVarToRuleStringMap map of build variable names to the list of
     * files assigned to the variable
     *
     * @param ruleBuffer buffer to add generated nmakefile text to
     *
     * @param folder the top level build output directory
     *
     * @param relativePath build output directory relative path of the current
     * output directory
     *
     * @param resource the source file for this invocation of the tool - this may be
     * null for a generated output
     *
     * @param sourceLocation the full path of the source
     *
     * @param resConfig the IResourceConfiguration associated with this file or null
     *
     * @param varName the build variable to add this invocation's outputs to if
     * <code>null</code>, use the file extension to find the name
     *
     * @param generatedSource if <code>true</code>, this file was generated by
     * another tool in the tool-chain
     */
    protected void addFragmentMakefileEntriesForSource(LinkedHashMap<String, String> buildVarToRuleStringMap,
            StringBuffer ruleBuffer, IFolder folder, String relativePath, IResource resource, IPath sourceLocation,
            IResourceInfo rcInfo, String varName, boolean generatedSource) {
        // Determine which tool, if any, builds files with this extension
        String ext = sourceLocation.getFileExtension();
        // jaba ignore .ino files
        if ("ino".equalsIgnoreCase(ext) || "pde".equalsIgnoreCase(ext))
            return;
        // end jaba ignore .ino files
        ITool tool = null;
        // TODO: remove
        // IResourceConfiguration resConfig = null;
        // if(rcInfo instanceof IFileInfo){
        // resConfig = (IFileInfo)rcInfo;
        // }
        // end remove
        // Use the tool from the resource configuration if there is one
        if (rcInfo instanceof IFileInfo) {
            IFileInfo fi = (IFileInfo) rcInfo;
            ITool[] tools = fi.getToolsToInvoke();
            if (tools != null && tools.length > 0) {
                tool = tools[0];
                // if(!tool.getCustomBuildStep())
                addToBuildVar(buildVarToRuleStringMap, ext, varName, relativePath, sourceLocation, generatedSource);
            }
        }
        ToolInfoHolder h = getToolInfo(rcInfo.getPath());
        ITool buildTools[] = h.buildTools;
        // if(tool == null){
        // for (int j=0; j<buildTools.length; j++) {
        // if (buildTools[j].buildsFileType(ext)) {
        // if (tool == null) {
        // tool = buildTools[j];
        // }
        // addToBuildVar(buildVarToRuleStringMap, ext, varName, relativePath,
        // sourceLocation, generatedSource);
        // break;
        // }
        // }
        // }
        //
        // if(tool == null && rcInfo.getPath().segmentCount() != 0){
        if (tool == null) {
            h = getToolInfo(Path.EMPTY);
            buildTools = h.buildTools;
            for (ITool buildTool : buildTools) {
                if (buildTool.buildsFileType(ext)) {
                    tool = buildTool;
                    addToBuildVar(buildVarToRuleStringMap, ext, varName, relativePath, sourceLocation, generatedSource);
                    break;
                }
            }
        }
        if (tool != null) {
            // Generate the rule to build this source file
            IInputType primaryInputType = tool.getPrimaryInputType();
            IInputType inputType = tool.getInputType(ext);
            if ((primaryInputType != null && !primaryInputType.getMultipleOfType())
                    || (inputType == null && tool != config.calculateTargetTool())) {
                // Try to add the rule for the file
                Vector<IPath> generatedOutputs = new Vector<IPath>();
                Vector<IPath> generatedDepFiles = new Vector<IPath>();
                // MODED moved JABA JAn Baeyens get the out type from the add
                // source call
                usedOutType = null;
                addRuleForSource(relativePath, ruleBuffer, resource, sourceLocation, rcInfo, generatedSource,
                        generatedDepFiles, generatedOutputs);
                // If the rule generates a dependency file(s), add the file(s)
                // to the variable
                if (generatedDepFiles.size() > 0) {
                    for (int k = 0; k < generatedDepFiles.size(); k++) {
                        IPath generatedDepFile = generatedDepFiles.get(k);
                        addMacroAdditionFile(buildVarToRuleStringMap, getDepMacroName(ext).toString(),
                                (generatedDepFile.isAbsolute() ? "" : DOT_SLASH_PATH.toOSString())
                                        + generatedDepFile.toOSString());
                    }
                }
                // If the generated outputs of this tool are input to another
                // tool,
                // 1. add the output to the appropriate macro
                // 2. If the tool does not have multipleOfType input, generate
                // the rule.
                // IOutputType outType = tool.getPrimaryOutputType();
                // MODED
                // moved JABA JAn Baeyens get the out type from the add source
                // call
                String buildVariable = null;
                if (usedOutType != null) {
                    if (tool.getCustomBuildStep()) {
                        // TODO: This is somewhat of a hack since a custom build
                        // step
                        // tool does not currently define a build variable
                        if (generatedOutputs.size() > 0) {
                            IPath firstOutput = generatedOutputs.get(0);
                            String firstExt = firstOutput.getFileExtension();
                            ToolInfoHolder tmpH = getFolderToolInfo(rcInfo.getPath());
                            ITool[] tmpBuildTools = tmpH.buildTools;
                            for (ITool tmpBuildTool : tmpBuildTools) {
                                if (tmpBuildTool.buildsFileType(firstExt)) {
                                    String bV = tmpBuildTool.getPrimaryInputType().getBuildVariable();
                                    if (bV.length() > 0) {
                                        buildVariable = bV;
                                        break;
                                    }
                                }
                            }
                        }
                    } else {
                        buildVariable = usedOutType.getBuildVariable();
                    }
                } else {
                    // For support of pre-CDT 3.0 integrations.
                    buildVariable = OBJS_MACRO;
                }
                for (int k = 0; k < generatedOutputs.size(); k++) {
                    IPath generatedOutput;
                    IResource generateOutputResource;
                    if (generatedOutputs.get(k).isAbsolute()) {
                        // TODO: Should we use relative paths when possible
                        // (e.g., see MbsMacroSupplier.calculateRelPath)
                        generatedOutput = generatedOutputs.get(k);
                        // If this file has an absolute path, then the
                        // generateOutputResource will not be correct
                        // because the file is not under the project. We use
                        // this resource in the calls to the dependency
                        // generator
                        generateOutputResource = project.getFile(generatedOutput);
                    } else {
                        generatedOutput = getPathForResource(project).append(getBuildWorkingDir())
                                .append(generatedOutputs.get(k));
                        generateOutputResource = project.getFile(getBuildWorkingDir().append(generatedOutputs.get(k)));
                    }
                    IResourceInfo nextRcInfo;
                    if (rcInfo instanceof IFileInfo) {
                        nextRcInfo = config.getResourceInfo(rcInfo.getPath().removeLastSegments(1), false);
                    } else {
                        nextRcInfo = rcInfo;
                    }
                    addFragmentMakefileEntriesForSource(buildVarToRuleStringMap, ruleBuffer, folder, relativePath,
                            generateOutputResource, generatedOutput, nextRcInfo, buildVariable, true);
                }
            }
        } else {
            // If this is a secondary input, add it to build vars
            if (varName == null) {
                for (ITool buildTool : buildTools) {
                    if (buildTool.isInputFileType(ext)) {
                        addToBuildVar(buildVarToRuleStringMap, ext, varName, relativePath, sourceLocation,
                                generatedSource);
                        break;
                    }
                }
            }
            // If this generated output is identified as a secondary output, add
            // the file to the build variable
            else {
                IOutputType[] secondaryOutputs = config.getToolChain().getSecondaryOutputs();
                if (secondaryOutputs.length > 0) {
                    if (isSecondaryOutputVar(h, secondaryOutputs, varName)) {
                        addMacroAdditionFile(buildVarToRuleStringMap, varName, relativePath, sourceLocation,
                                generatedSource);
                    }
                }
            }
        }
    }

    /**
     * Gets a path for a resource by extracting the Path field from its location
     * URI.
     *
     * @return IPath
     * @since 6.0
     */

    protected IPath getPathForResource(IResource resource) {
        return new Path(resource.getLocationURI().getPath());
    }

    /**
     * Adds the source file to the appropriate build variable
     *
     * @param buildVarToRuleStringMap
     *            map of build variable names to the list of files assigned to the
     *            variable
     * @param ext
     *            the file extension of the file
     * @param varName
     *            the build variable to add this invocation's outputs to if
     *            <code>null</code>, use the file extension to find the name
     * @param relativePath
     *            build output directory relative path of the current output
     *            directory
     * @param sourceLocation
     *            the full path of the source
     * @param generatedSource
     *            if <code>true</code>, this file was generated by another tool in
     *            the tool-chain
     */
    protected void addToBuildVar(LinkedHashMap<String, String> buildVarToRuleStringMap, String ext, String varName,
            String relativePath, IPath sourceLocation, boolean generatedSource) {
        List<IPath> varList = null;
        if (varName == null) {
            // Get the proper source build variable based upon the extension
            varName = getSourceMacroName(ext).toString();
            varList = buildSrcVars.get(varName);
        } else {
            varList = buildOutVars.get(varName);
        }
        // Add the resource to the list of all resources associated with a
        // variable.
        // Do not allow duplicates - there is no reason to and it can be 'bad' -
        // e.g., having the same object in the OBJS list can cause duplicate
        // symbol errors from the linker
        if ((varList != null) && !(varList.contains(sourceLocation))) {
            // Since we don't know how these files will be used, we store them
            // using a "location"
            // path rather than a relative path
            varList.add(sourceLocation);
            if (!buildVarToRuleStringMap.containsKey(varName)) {
                // TODO - is this an error?
            } else {
                // Add the resource name to the makefile line that adds
                // resources to the build variable
                addMacroAdditionFile(buildVarToRuleStringMap, varName, relativePath, sourceLocation, generatedSource);
            }
        }
    }

    private IManagedCommandLineInfo generateToolCommandLineInfo(ITool tool, String sourceExtension, String[] flags,
            String outputFlag, String outputPrefix, String outputName, String[] inputResources, IPath inputLocation,
            IPath outputLocation) {
        String cmd = tool.getToolCommand();
        // try to resolve the build macros in the tool command
        try {
            String resolvedCommand = null;
            if ((inputLocation != null && inputLocation.toString().indexOf(" ") != -1)
                    || (outputLocation != null && outputLocation.toString().indexOf(" ") != -1)) {
                resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValue(cmd, "", " ",
                        IBuildMacroProvider.CONTEXT_FILE,
                        new FileContextData(inputLocation, outputLocation, null, tool));
            } else {
                resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(cmd, "", " ",
                        IBuildMacroProvider.CONTEXT_FILE,
                        new FileContextData(inputLocation, outputLocation, null, tool));
            }
            if ((resolvedCommand = resolvedCommand.trim()).length() > 0)
                cmd = resolvedCommand;
        } catch (BuildMacroException e) {
            /* JABA is not going to write this code */
        }
        IManagedCommandLineGenerator gen = tool.getCommandLineGenerator();
        return gen.generateCommandLineInfo(tool, cmd, flags, outputFlag, outputPrefix, outputName, inputResources,
                tool.getCommandLinePattern());
    }

    /**
     * Create a rule for this source file. We create a pattern rule if possible.
     * This is an example of a pattern rule: <relative_path>/%.<outputExtension>:
     * ../<relative_path>/%. <inputExtension>
     *
     * @echo Building file: $<
     * @echo Invoking tool xxx
     * @echo <tool> <flags> <output_flag><output_prefix>$@ $<
     * @<tool> <flags> <output_flag><output_prefix>$@ $< && \ echo -n $(@:%.o=%.d) '
     *         <relative_path>/' >> $(@:%.o=%.d) && \ <tool> -P -MM -MG <flags> $<
     *         >> $(@:%.o=%.d)
     * @echo Finished building: $<
     * @echo ' ' Note that the macros all come from the build model and are resolved
     *       to a real command before writing to the module makefile, so a real
     *       command might look something like: source1/%.o: ../source1/%.cpp
     * @echo Building file: $<
     * @echo Invoking tool xxx
     * @echo g++ -g -O2 -c -I/cygdrive/c/eclipse/workspace/Project/headers -o$@
     *       $< @g++ -g -O2 -c -I/cygdrive/c/eclipse/workspace/Project/headers -o$@
     *       $< && \ echo -n $(@:%.o=%.d) ' source1/' >> $(@:%.o=%.d) && \ g++ -P
     *       -MM -MG -g -O2 -c -I/cygdrive/c/eclipse/workspace/Project/headers $< >>
     *       $(@:%.o=%.d)
     * @echo Finished building: $<
     * @echo ' '
     * @param relativePath
     *            top build output directory relative path of the current output
     *            directory
     * @param buffer
     *            buffer to populate with the build rule
     * @param resource
     *            the source file for this invocation of the tool
     * @param sourceLocation
     *            the full path of the source
     * @param rcInfo
     *            the IResourceInfo associated with this file or null
     * @param generatedSource
     *            <code>true</code> if the resource is a generated output
     * @param enumeratedOutputs
     *            vector of the filenames that are the output of this rule
     */
    protected void addRuleForSource(String relativePath, StringBuffer buffer, IResource resource, IPath sourceLocation,
            IResourceInfo rcInfo, boolean generatedSource, List<IPath> generatedDepFiles,
            List<IPath> enumeratedOutputs) {
        String fileName = sourceLocation.removeFileExtension().lastSegment();
        String inputExtension = sourceLocation.getFileExtension();
        String outputExtension = null;
        ITool tool = null;
        if (rcInfo instanceof IFileInfo) {
            IFileInfo fi = (IFileInfo) rcInfo;
            ITool[] tools = fi.getToolsToInvoke();
            if (tools != null && tools.length > 0) {
                tool = tools[0];
            }
        } else {
            IFolderInfo foInfo = (IFolderInfo) rcInfo;
            tool = foInfo.getToolFromInputExtension(inputExtension);
        }
        ToolInfoHolder h = getToolInfo(rcInfo.getPath());
        if (tool != null)
            outputExtension = tool.getOutputExtension(inputExtension);
        if (outputExtension == null)
            outputExtension = EMPTY_STRING;
        // Get the dependency generator information for this tool and file
        // extension
        IManagedDependencyGenerator oldDepGen = null;
        IManagedDependencyGenerator2 depGen = null;
        IManagedDependencyInfo depInfo = null;
        IManagedDependencyCommands depCommands = null;
        IManagedDependencyPreBuild depPreBuild = null;
        IPath[] depFiles = null;
        boolean doDepGen = false;
        {
            IManagedDependencyGeneratorType t = null;
            if (tool != null)
                t = tool.getDependencyGeneratorForExtension(inputExtension);
            if (t != null) {
                int calcType = t.getCalculatorType();
                if (calcType <= IManagedDependencyGeneratorType.TYPE_OLD_TYPE_LIMIT) {
                    oldDepGen = (IManagedDependencyGenerator) t;
                    doDepGen = (calcType == IManagedDependencyGeneratorType.TYPE_COMMAND);
                    if (doDepGen) {
                        IPath depFile = Path.fromOSString(relativePath + fileName + DOT + DEP_EXT);
                        getDependencyMakefiles(h).add(depFile);
                        generatedDepFiles.add(depFile);
                    }
                } else {
                    depGen = (IManagedDependencyGenerator2) t;
                    doDepGen = (calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS);
                    IBuildObject buildContext = rcInfo;
                    depInfo = depGen.getDependencySourceInfo(resource.getProjectRelativePath(), resource, buildContext,
                            tool, getBuildWorkingDir());
                    if (calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS) {
                        depCommands = (IManagedDependencyCommands) depInfo;
                        depFiles = depCommands.getDependencyFiles();
                    } else if (calcType == IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS) {
                        depPreBuild = (IManagedDependencyPreBuild) depInfo;
                        depFiles = depPreBuild.getDependencyFiles();
                    }
                    if (depFiles != null) {
                        for (IPath depFile : depFiles) {
                            getDependencyMakefiles(h).add(depFile);
                            generatedDepFiles.add(depFile);
                        }
                    }
                }
            }
        }
        // Figure out the output paths
        String optDotExt = EMPTY_STRING;
        if (outputExtension.length() > 0)
            optDotExt = DOT + outputExtension;
        Vector<IPath> ruleOutputs = new Vector<IPath>();
        Vector<IPath> enumeratedPrimaryOutputs = new Vector<IPath>();
        Vector<IPath> enumeratedSecondaryOutputs = new Vector<IPath>();
        // JABA
        usedOutType = tool.getPrimaryOutputType();
        calculateOutputsForSource(tool, relativePath, resource, sourceLocation, ruleOutputs, enumeratedPrimaryOutputs,
                enumeratedSecondaryOutputs);
        enumeratedOutputs.addAll(enumeratedPrimaryOutputs);
        enumeratedOutputs.addAll(enumeratedSecondaryOutputs);
        String primaryOutputName = null;
        if (enumeratedPrimaryOutputs.size() > 0) {
            primaryOutputName = escapeWhitespaces(enumeratedPrimaryOutputs.get(0).toOSString());
        } else {
            primaryOutputName = escapeWhitespaces(relativePath + fileName + optDotExt);
        }
        String otherPrimaryOutputs = EMPTY_STRING;
        for (int i = 1; i < enumeratedPrimaryOutputs.size(); i++) {
            otherPrimaryOutputs += WHITESPACE + escapeWhitespaces(enumeratedPrimaryOutputs.get(i).toOSString());
        }
        // Output file location needed for the file-specific build macros
        IPath outputLocation = Path.fromOSString(primaryOutputName);
        if (!outputLocation.isAbsolute()) {
            outputLocation = getPathForResource(project).append(getBuildWorkingDir()).append(primaryOutputName);
        }
        // A separate rule is needed for the resource in the case where explicit
        // file-specific macros
        // are referenced, or if the resource contains special characters in its
        // path (e.g., whitespace)
        /*
         * fix for 137674 We only need an explicit rule if one of the following is true:
         * - The resource is linked, and its full path to its real location contains
         * special characters - The resource is not linked, but its project relative
         * path contains special characters
         */
        boolean resourceNameRequiresExplicitRule = (resource.isLinked()
                && containsSpecialCharacters(sourceLocation.toOSString()))
                || (!resource.isLinked() && containsSpecialCharacters(resource.getProjectRelativePath().toOSString()));
        boolean needExplicitRuleForFile = resourceNameRequiresExplicitRule
                || BuildMacroProvider.getReferencedExplitFileMacros(tool).length > 0
                || BuildMacroProvider.getReferencedExplitFileMacros(tool.getToolCommand(),
                        IBuildMacroProvider.CONTEXT_FILE,
                        new FileContextData(sourceLocation, outputLocation, null, tool)).length > 0;
        // Get and resolve the command
        String cmd = tool.getToolCommand();
        try {
            String resolvedCommand = null;
            if (!needExplicitRuleForFile) {
                resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(cmd,
                        EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
                        new FileContextData(sourceLocation, outputLocation, null, tool));
            } else {
                // if we need an explicit rule then don't use any builder
                // variables, resolve everything
                // to explicit strings
                resolvedCommand = ManagedBuildManager.getBuildMacroProvider().resolveValue(cmd, EMPTY_STRING,
                        WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
                        new FileContextData(sourceLocation, outputLocation, null, tool));
            }
            if ((resolvedCommand = resolvedCommand.trim()).length() > 0)
                cmd = resolvedCommand;
        } catch (BuildMacroException e) {
            /* JABA is not going to write this code */
        }
        String defaultOutputName = EMPTY_STRING;
        String primaryDependencyName = EMPTY_STRING;
        String patternPrimaryDependencyName = EMPTY_STRING;
        String home = (generatedSource) ? DOT : ROOT;
        String resourcePath = null;
        boolean patternRule = true;
        boolean isItLinked = false;
        if (resource.isLinked(IResource.CHECK_ANCESTORS)) {
            // it IS linked, so use the actual location
            isItLinked = true;
            resourcePath = sourceLocation.toOSString();
            // Need a hardcoded rule, not a pattern rule, as a linked file
            // can reside in any path
            defaultOutputName = escapeWhitespaces(relativePath + fileName + optDotExt);
            primaryDependencyName = escapeWhitespaces(resourcePath);
            patternRule = false;
        } else {
            // Use the relative path (not really needed to store per se but in
            // the future someone may want this)
            resourcePath = relativePath;
            // The rule and command to add to the makefile
            if (rcInfo instanceof IFileInfo || needExplicitRuleForFile) {
                // Need a hardcoded rule, not a pattern rule
                defaultOutputName = escapeWhitespaces(resourcePath + fileName + optDotExt);
                patternRule = false;
            } else {
                defaultOutputName = relativePath + WILDCARD + optDotExt;
            }
            primaryDependencyName = escapeWhitespaces(
                    home + FILE_SEPARATOR + resourcePath + fileName + DOT + inputExtension);
            patternPrimaryDependencyName = home + FILE_SEPARATOR + resourcePath + WILDCARD + DOT + inputExtension;
        } // end fix for PR 70491
          // If the tool specifies a dependency calculator of
          // TYPE_BUILD_COMMANDS,
          // ask whether
          // the dependency commands are "generic" (i.e., we can use a pattern
          // rule)
        boolean needExplicitDependencyCommands = false;
        if (depCommands != null) {
            needExplicitDependencyCommands = !depCommands.areCommandsGeneric();
        }
        // If we still think that we are using a pattern rule, check a few more
        // things
        if (patternRule) {
            patternRule = false;
            // Make sure that at least one of the rule outputs contains a %.
            for (int i = 0; i < ruleOutputs.size(); i++) {
                String ruleOutput = ruleOutputs.get(i).toOSString();
                if (ruleOutput.indexOf('%') >= 0) {
                    patternRule = true;
                    break;
                }
            }
            if (patternRule) {
                patternRule = !needExplicitDependencyCommands;
            }
        }
        // Begin building the rule for this source file
        String buildRule = EMPTY_STRING;
        if (patternRule) {
            if (ruleOutputs.size() == 0) {
                buildRule += defaultOutputName;
            } else {
                boolean first = true;
                for (int i = 0; i < ruleOutputs.size(); i++) {
                    String ruleOutput = ruleOutputs.get(i).toOSString();
                    if (ruleOutput.indexOf('%') >= 0) {
                        if (first) {
                            first = false;
                        } else {
                            buildRule += WHITESPACE;
                        }
                        buildRule += ruleOutput;
                    }
                }
            }
        } else {
            buildRule += primaryOutputName;
        }
        String buildRuleDependencies = primaryDependencyName;
        String patternBuildRuleDependencies = patternPrimaryDependencyName;
        // Other additional inputs
        // Get any additional dependencies specified for the tool in other
        // InputType elements and AdditionalInput elements
        IPath[] addlDepPaths = tool.getAdditionalDependencies();
        for (IPath addlDepPath : addlDepPaths) {
            // Translate the path from project relative to build directory
            // relative
            IPath addlPath = addlDepPath;
            if (!(addlPath.toString().startsWith("$("))) {
                if (!addlPath.isAbsolute()) {
                    IPath tempPath = project.getLocation().append(new Path(ensureUnquoted(addlPath.toString())));
                    if (tempPath != null) {
                        addlPath = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), tempPath);
                    }
                }
            }
            String suitablePath = ensurePathIsGNUMakeTargetRuleCompatibleSyntax(addlPath.toOSString());
            buildRuleDependencies += WHITESPACE + suitablePath;
            patternBuildRuleDependencies += WHITESPACE + suitablePath;
        }
        buildRule += COLON + WHITESPACE + (patternRule ? patternBuildRuleDependencies : buildRuleDependencies);
        // No duplicates in a makefile. If we already have this rule, don't add
        // it or the commands to build the file
        if (getRuleList().contains(buildRule)) {
            // TODO: Should we assert that this is a pattern rule?
        } else {
            getRuleList().add(buildRule);
            // Echo starting message
            buffer.append(buildRule).append(NEWLINE);
            buffer.append(TAB).append(AT).append(escapedEcho(MESSAGE_START_FILE + WHITESPACE + IN_MACRO));
            buffer.append(TAB).append(AT).append(escapedEcho(tool.getAnnouncement()));
            // If the tool specifies a dependency calculator of
            // TYPE_BUILD_COMMANDS, ask whether
            // there are any pre-tool commands.
            if (depCommands != null) {
                String[] preToolCommands = depCommands.getPreToolDependencyCommands();
                if (preToolCommands != null && preToolCommands.length > 0) {
                    for (String preCmd : preToolCommands) {
                        try {
                            String resolvedCommand;
                            IBuildMacroProvider provider = ManagedBuildManager.getBuildMacroProvider();
                            if (!needExplicitRuleForFile) {
                                resolvedCommand = provider.resolveValueToMakefileFormat(preCmd, EMPTY_STRING,
                                        WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
                                        new FileContextData(sourceLocation, outputLocation, null, tool));
                            } else {
                                // if we need an explicit rule then don't use
                                // any builder
                                // variables, resolve everything to explicit
                                // strings
                                resolvedCommand = provider.resolveValue(preCmd, EMPTY_STRING, WHITESPACE,
                                        IBuildMacroProvider.CONTEXT_FILE,
                                        new FileContextData(sourceLocation, outputLocation, null, tool));
                            }
                            if (resolvedCommand != null)
                                buffer.append(resolvedCommand).append(NEWLINE);
                        } catch (BuildMacroException e) {
                            /* JABA is not going to write this code */
                        }
                    }
                }
            }
            // Generate the command line
            Vector<String> inputs = new Vector<String>();
            inputs.add(IN_MACRO);
            // Other additional inputs
            // Get any additional dependencies specified for the tool in other
            // InputType elements and AdditionalInput elements
            IPath[] addlInputPaths = getAdditionalResourcesForSource(tool);
            for (IPath addlInputPath : addlInputPaths) {
                // Translate the path from project relative to build directory
                // relative
                IPath addlPath = addlInputPath;
                if (!(addlPath.toString().startsWith("$("))) {
                    if (!addlPath.isAbsolute()) {
                        IPath tempPath = getPathForResource(project).append(addlPath);
                        if (tempPath != null) {
                            addlPath = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), tempPath);
                        }
                    }
                }
                inputs.add(addlPath.toOSString());
            }
            String[] inputStrings = inputs.toArray(new String[inputs.size()]);
            String[] flags = null;
            // Get the tool command line options
            try {
                flags = tool.getToolCommandFlags(sourceLocation, outputLocation);
            } catch (BuildException ex) {
                // TODO add some routines to catch this
                flags = EMPTY_STRING_ARRAY;
            }
            // If we have a TYPE_BUILD_COMMANDS dependency generator, determine
            // if there are any options that
            // it wants added to the command line
            if (depCommands != null) {
                flags = addDependencyOptions(depCommands, flags);
            }
            IManagedCommandLineInfo cmdLInfo = null;
            String outflag = null;
            String outputPrefix = null;
            if (rcInfo instanceof IFileInfo || needExplicitRuleForFile || needExplicitDependencyCommands) {
                outflag = tool.getOutputFlag();
                outputPrefix = tool.getOutputPrefix();
                // Call the command line generator
                IManagedCommandLineGenerator cmdLGen = tool.getCommandLineGenerator();
                cmdLInfo = cmdLGen.generateCommandLineInfo(tool, cmd, flags, outflag, outputPrefix,
                        OUT_MACRO + otherPrimaryOutputs, inputStrings, tool.getCommandLinePattern());
            } else {
                outflag = tool.getOutputFlag();
                outputPrefix = tool.getOutputPrefix();
                // Call the command line generator
                cmdLInfo = generateToolCommandLineInfo(tool, inputExtension, flags, outflag, outputPrefix,
                        OUT_MACRO + otherPrimaryOutputs, inputStrings, sourceLocation, outputLocation);
            }
            // The command to build
            String buildCmd;
            if (cmdLInfo != null) {
                buildCmd = cmdLInfo.getCommandLine();
            } else {
                StringBuffer buildFlags = new StringBuffer();
                for (String flag : flags) {
                    if (flag != null) {
                        buildFlags.append(flag).append(WHITESPACE);
                    }
                }
                buildCmd = cmd + WHITESPACE + buildFlags.toString().trim() + WHITESPACE + outflag + WHITESPACE
                        + outputPrefix + OUT_MACRO + otherPrimaryOutputs + WHITESPACE + IN_MACRO;
            }
            // resolve any remaining macros in the command after it has been
            // generated
            try {
                String resolvedCommand;
                IBuildMacroProvider provider = ManagedBuildManager.getBuildMacroProvider();
                if (!needExplicitRuleForFile) {
                    resolvedCommand = provider.resolveValueToMakefileFormat(buildCmd, EMPTY_STRING, WHITESPACE,
                            IBuildMacroProvider.CONTEXT_FILE,
                            new FileContextData(sourceLocation, outputLocation, null, tool));
                } else {
                    // if we need an explicit rule then don't use any builder
                    // variables, resolve everything to explicit strings
                    resolvedCommand = provider.resolveValue(buildCmd, EMPTY_STRING, WHITESPACE,
                            IBuildMacroProvider.CONTEXT_FILE,
                            new FileContextData(sourceLocation, outputLocation, null, tool));
                }
                if ((resolvedCommand = resolvedCommand.trim()).length() > 0)
                    buildCmd = resolvedCommand;
            } catch (BuildMacroException e) {
                /* JABA is not going to write this code */
            }
            // buffer.append(TAB).append(AT).append(escapedEcho(buildCmd));
            // buffer.append(TAB).append(AT).append(buildCmd);
            // JABA add sketch.prebuild and postbouild if needed
            if ("sloeber.ino".equals(fileName)) {
                ICConfigurationDescription confDesc = ManagedBuildManager.getDescriptionForConfiguration(config);
                String sketchPrebuild = io.sloeber.core.common.Common.getBuildEnvironmentVariable(confDesc,
                        "sloeber.sketch.prebuild", new String(), true);
                String sketchPostBuild = io.sloeber.core.common.Common.getBuildEnvironmentVariable(confDesc,
                        "sloeber.sketch.postbuild", new String(), true);
                if (!sketchPrebuild.isEmpty()) {
                    buffer.append(TAB).append(sketchPrebuild);
                }
                buffer.append(TAB).append(buildCmd).append(NEWLINE);
                if (!sketchPostBuild.isEmpty()) {
                    buffer.append(TAB).append(sketchPostBuild);
                }
            } else {
                buffer.append(TAB).append(buildCmd);
            }
            // end JABA add sketch.prebuild and postbouild if needed

            // Determine if there are any dependencies to calculate
            if (doDepGen) {
                // Get the dependency rule out of the generator
                String[] depCmds = null;
                if (oldDepGen != null) {
                    depCmds = new String[1];
                    depCmds[0] = oldDepGen.getDependencyCommand(resource, ManagedBuildManager.getBuildInfo(project));
                } else {
                    if (depCommands != null) {
                        depCmds = depCommands.getPostToolDependencyCommands();
                    }
                }
                if (depCmds != null) {
                    for (String depCmd : depCmds) {
                        // Resolve any macros in the dep command after it has
                        // been generated.
                        // Note: do not trim the result because it will strip
                        // out necessary tab characters.
                        buffer.append(WHITESPACE).append(LOGICAL_AND).append(WHITESPACE).append(LINEBREAK);
                        try {
                            if (!needExplicitRuleForFile) {
                                depCmd = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
                                        depCmd, EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
                                        new FileContextData(sourceLocation, outputLocation, null, tool));
                            } else {
                                depCmd = ManagedBuildManager.getBuildMacroProvider().resolveValue(depCmd, EMPTY_STRING,
                                        WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
                                        new FileContextData(sourceLocation, outputLocation, null, tool));
                            }
                        } catch (BuildMacroException e) {
                            /* JABA is not going to do this */
                        }
                        buffer.append(depCmd);
                    }
                }
            }
            // Echo finished message
            buffer.append(NEWLINE);
            buffer.append(TAB).append(AT).append(escapedEcho(MESSAGE_FINISH_FILE + WHITESPACE + IN_MACRO));
            buffer.append(TAB).append(AT).append(ECHO_BLANK_LINE).append(NEWLINE);
        }
        // Determine if there are calculated dependencies
        IPath[] addlDeps = null;
        IPath[] addlTargets = null;
        String calculatedDependencies = null;
        boolean addedDepLines = false;
        String depLine;
        if (oldDepGen != null && oldDepGen.getCalculatorType() != IManagedDependencyGeneratorType.TYPE_COMMAND) {
            addlDeps = oldCalculateDependenciesForSource(oldDepGen, tool, relativePath, resource);
        } else {
            if (depGen != null && depGen.getCalculatorType() == IManagedDependencyGeneratorType.TYPE_CUSTOM) {
                if (depInfo instanceof IManagedDependencyCalculator) {
                    IManagedDependencyCalculator depCalculator = (IManagedDependencyCalculator) depInfo;
                    addlDeps = calculateDependenciesForSource(depCalculator);
                    addlTargets = depCalculator.getAdditionalTargets();
                }
            }
        }
        if (addlDeps != null && addlDeps.length > 0) {
            calculatedDependencies = "";
            for (IPath addlDep : addlDeps) {
                calculatedDependencies += WHITESPACE + escapeWhitespaces(addlDep.toOSString());
            }
        }
        if (calculatedDependencies != null) {
            depLine = primaryOutputName + COLON + calculatedDependencies + NEWLINE;
            if (!getDepLineList().contains(depLine)) {
                getDepLineList().add(depLine);
                addedDepLines = true;
                buffer.append(depLine);
            }
        }
        // Add any additional outputs here using dependency lines
        Vector<IPath> addlOutputs = new Vector<IPath>();
        if (enumeratedPrimaryOutputs.size() > 1) {
            // Starting with 1 is intentional in order to skip the primary
            // output
            for (int i = 1; i < enumeratedPrimaryOutputs.size(); i++)
                addlOutputs.add(enumeratedPrimaryOutputs.get(i));
        }
        addlOutputs.addAll(enumeratedSecondaryOutputs);
        if (addlTargets != null) {
            for (IPath addlTarget : addlTargets)
                addlOutputs.add(addlTarget);
        }
        for (int i = 0; i < addlOutputs.size(); i++) {
            depLine = escapeWhitespaces(addlOutputs.get(i).toOSString()) + COLON + WHITESPACE + primaryOutputName;
            if (calculatedDependencies != null)
                depLine += calculatedDependencies;
            depLine += NEWLINE;
            if (!getDepLineList().contains(depLine)) {
                getDepLineList().add(depLine);
                addedDepLines = true;
                buffer.append(depLine);
            }
        }
        if (addedDepLines) {
            buffer.append(NEWLINE);
        }
        // If we are using a dependency calculator of type
        // TYPE_PREBUILD_COMMANDS,
        // get the rule to build the dependency file
        if (depPreBuild != null && depFiles != null) {
            addedDepLines = false;
            String[] preBuildCommands = depPreBuild.getDependencyCommands();
            if (preBuildCommands != null) {
                depLine = "";
                // Can we use a pattern rule?
                patternRule = !isItLinked && !needExplicitRuleForFile && depPreBuild.areCommandsGeneric();
                // Begin building the rule
                for (int i = 0; i < depFiles.length; i++) {
                    if (i > 0)
                        depLine += WHITESPACE;
                    if (patternRule) {
                        optDotExt = EMPTY_STRING;
                        String depExt = depFiles[i].getFileExtension();
                        if (depExt != null && depExt.length() > 0)
                            optDotExt = DOT + depExt;
                        depLine += escapeWhitespaces(relativePath + WILDCARD + optDotExt);
                    } else {
                        depLine += escapeWhitespaces((depFiles[i]).toOSString());
                    }
                }
                depLine += COLON + WHITESPACE + (patternRule ? patternBuildRuleDependencies : buildRuleDependencies);
                if (!getDepRuleList().contains(depLine)) {
                    getDepRuleList().add(depLine);
                    addedDepLines = true;
                    buffer.append(depLine).append(NEWLINE);
                    buffer.append(TAB).append(AT)
                            .append(escapedEcho(MESSAGE_START_DEPENDENCY + WHITESPACE + OUT_MACRO));
                    for (String preBuildCommand : preBuildCommands) {
                        depLine = preBuildCommand;
                        // Resolve macros
                        try {
                            if (!needExplicitRuleForFile) {
                                depLine = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
                                        depLine, EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
                                        new FileContextData(sourceLocation, outputLocation, null, tool));
                            } else {
                                depLine = ManagedBuildManager.getBuildMacroProvider().resolveValue(depLine,
                                        EMPTY_STRING, WHITESPACE, IBuildMacroProvider.CONTEXT_FILE,
                                        new FileContextData(sourceLocation, outputLocation, null, tool));
                            }
                        } catch (BuildMacroException e) {
                            // JABA is not going to write this code
                        }
                        // buffer.append(TAB + AT + escapedEcho(depLine));
                        // buffer.append(TAB + AT + depLine + NEWLINE);
                        buffer.append(TAB).append(depLine).append(NEWLINE);
                    }
                }
                if (addedDepLines) {
                    buffer.append(TAB).append(AT).append(ECHO_BLANK_LINE).append(NEWLINE);
                }
            }
        }
    }

    /*
     * Add any dependency calculator options to the tool options
     */
    private String[] addDependencyOptions(IManagedDependencyCommands depCommands, String[] flags) {
        String[] depOptions = depCommands.getDependencyCommandOptions();
        if (depOptions != null && depOptions.length > 0) {
            int flagsLen = flags.length;
            String[] flagsCopy = new String[flags.length + depOptions.length];
            for (int i = 0; i < flags.length; i++) {
                flagsCopy[i] = flags[i];
            }
            for (int i = 0; i < depOptions.length; i++) {
                flagsCopy[i + flagsLen] = depOptions[i];
            }
            flags = flagsCopy;
        }
        return flags;
    }

    /**
     * Returns any additional resources specified for the tool in other InputType
     * elements and AdditionalInput elements
     */
    protected IPath[] getAdditionalResourcesForSource(ITool tool) {
        List<IPath> allRes = new ArrayList<>();
        IInputType[] types = tool.getInputTypes();
        for (IInputType type : types) {
            // Additional resources come from 2 places.
            // 1. From AdditionalInput childen
            IPath[] res = type.getAdditionalResources();
            for (IPath re : res) {
                allRes.add(re);
            }
            // 2. From InputTypes that other than the primary input type
            if (!type.getPrimaryInput() && type != tool.getPrimaryInputType()) {
                String var = type.getBuildVariable();
                if (var != null && var.length() > 0) {
                    allRes.add(Path.fromOSString("$(" + type.getBuildVariable() + ")"));
                } else {
                    // Use file extensions
                    String[] typeExts = type.getSourceExtensions(tool);
                    for (IResource projectResource : projectResources) {
                        if (projectResource.getType() == IResource.FILE) {
                            String fileExt = projectResource.getFileExtension();
                            if (fileExt == null) {
                                fileExt = "";
                            }
                            for (String typeExt : typeExts) {
                                if (fileExt.equals(typeExt)) {
                                    allRes.add(projectResource.getProjectRelativePath());
                                    break;
                                }
                            }
                        }
                    }
                }
                // If an assignToOption has been specified, set the value of the
                // option to the inputs
                IOption assignToOption = tool.getOptionBySuperClassId(type.getAssignToOptionId());
                IOption option = tool.getOptionBySuperClassId(type.getOptionId());
                if (assignToOption != null && option == null) {
                    try {
                        int optType = assignToOption.getValueType();
                        IResourceInfo rcInfo = tool.getParentResourceInfo();
                        if (rcInfo != null) {
                            if (optType == IOption.STRING) {
                                String optVal = "";
                                for (int j = 0; j < allRes.size(); j++) {
                                    if (j != 0) {
                                        optVal += " ";
                                    }
                                    String resPath = allRes.get(j).toString();
                                    if (!resPath.startsWith("$(")) {
                                        IResource addlResource = project.getFile(resPath);
                                        if (addlResource != null) {
                                            IPath addlPath = addlResource.getLocation();
                                            if (addlPath != null) {
                                                resPath = ManagedBuildManager
                                                        .calculateRelativePath(getTopBuildDir(), addlPath).toString();
                                            }
                                        }
                                    }
                                    optVal += ManagedBuildManager
                                            .calculateRelativePath(getTopBuildDir(), Path.fromOSString(resPath))
                                            .toString();
                                }
                                ManagedBuildManager.setOption(rcInfo, tool, assignToOption, optVal);
                            } else if (optType == IOption.STRING_LIST || optType == IOption.LIBRARIES
                                    || optType == IOption.OBJECTS || optType == IOption.INCLUDE_FILES
                                    || optType == IOption.LIBRARY_PATHS || optType == IOption.LIBRARY_FILES
                                    || optType == IOption.MACRO_FILES) {
                                // TODO: do we need to do anything with undefs
                                // here?
                                // Note that the path(s) must be translated from
                                // project relative
                                // to top build directory relative
                                String[] paths = new String[allRes.size()];
                                for (int j = 0; j < allRes.size(); j++) {
                                    paths[j] = allRes.get(j).toString();
                                    if (!paths[j].startsWith("$(")) {
                                        IResource addlResource = project.getFile(paths[j]);
                                        if (addlResource != null) {
                                            IPath addlPath = addlResource.getLocation();
                                            if (addlPath != null) {
                                                paths[j] = ManagedBuildManager
                                                        .calculateRelativePath(getTopBuildDir(), addlPath).toString();
                                            }
                                        }
                                    }
                                }
                                ManagedBuildManager.setOption(rcInfo, tool, assignToOption, paths);
                            } else if (optType == IOption.BOOLEAN) {
                                boolean b = false;
                                if (allRes.size() > 0)
                                    b = true;
                                ManagedBuildManager.setOption(rcInfo, tool, assignToOption, b);
                            } else if (optType == IOption.ENUMERATED || optType == IOption.TREE) {
                                if (allRes.size() > 0) {
                                    String s = allRes.get(0).toString();
                                    ManagedBuildManager.setOption(rcInfo, tool, assignToOption, s);
                                }
                            }
                            allRes.clear();
                        }
                    } catch (BuildException ex) {
                        /* JABA is not going to write this code */
                    }
                }
            }
        }
        return allRes.toArray(new IPath[allRes.size()]);
    }

    /**
     * Returns the output <code>IPath</code>s for this invocation of the tool with
     * the specified source file The priorities for determining the names of the
     * outputs of a tool are: 1. If the tool is the build target and primary output,
     * use artifact name & extension - This case does not apply here... 2. If an
     * option is specified, use the value of the option 3. If a nameProvider is
     * specified, call it 4. If outputNames is specified, use it 5. Use the name
     * pattern to generate a transformation macro so that the source names can be
     * transformed into the target names using the built-in string substitution
     * functions of <code>make</code>.
     *
     * @param relativePath
     *            build output directory relative path of the current output
     *            directory
     * @param ruleOutputs
     *            Vector of rule IPaths that are relative to the build directory
     * @param enumeratedPrimaryOutputs
     *            Vector of IPaths of primary outputs that are relative to the build
     *            directory
     * @param enumeratedSecondaryOutputs
     *            Vector of IPaths of secondary outputs that are relative to the
     *            build directory
     */
    protected void calculateOutputsForSource(ITool tool, String relativePath, IResource resource, IPath sourceLocation,
            Vector<IPath> ruleOutputs, Vector<IPath> enumeratedPrimaryOutputs,
            Vector<IPath> enumeratedSecondaryOutputs) {
        String inExt = sourceLocation.getFileExtension();
        String outExt = tool.getOutputExtension(inExt);
        // IResourceInfo rcInfo = tool.getParentResourceInfo();
        IOutputType[] outTypes = tool.getOutputTypes();
        if (outTypes != null && outTypes.length > 0) {
            for (IOutputType type : outTypes) {
                boolean primaryOutput = (type == tool.getPrimaryOutputType());
                // if (primaryOutput && ignorePrimary) continue;
                String outputPrefix = type.getOutputPrefix();
                // Resolve any macros in the outputPrefix
                // Note that we cannot use file macros because if we do a clean
                // we need to know the actual name of the file to clean, and
                // cannot use any builder variables such as $@. Hence we use the
                // next best thing, i.e. configuration context.
                // figure out the configuration we're using
                // IBuildObject toolParent = tool.getParent();
                // IConfiguration config = null;
                // if the parent is a config then we're done
                // if (toolParent instanceof IConfiguration)
                // config = (IConfiguration) toolParent;
                // else if (toolParent instanceof IToolChain) {
                // // must be a toolchain
                // config = (IConfiguration) ((IToolChain) toolParent)
                // .getParent();
                // }
                //
                // else if (toolParent instanceof IResourceConfiguration) {
                // config = (IConfiguration) ((IResourceConfiguration)
                // toolParent)
                // .getParent();
                // }
                // else {
                // // bad
                // throw new AssertionError(
                // "tool parent must be one of configuration, toolchain, or
                // resource configuration");
                // }
                // if (config != null) {
                try {
                    if (containsSpecialCharacters(sourceLocation.toOSString())) {
                        outputPrefix = ManagedBuildManager.getBuildMacroProvider().resolveValue(outputPrefix, "", " ",
                                IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
                    } else {
                        outputPrefix = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
                                outputPrefix, "", " ", IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
                    }
                } catch (BuildMacroException e) {
                    /* JABA is not going to write this code */
                }
                // }
                boolean multOfType = type.getMultipleOfType();
                IOption option = tool.getOptionBySuperClassId(type.getOptionId());
                IManagedOutputNameProvider nameProvider = type.getNameProvider();
                String[] outputNames = type.getOutputNames();
                // 1. If the tool is the build target and this is the primary
                // output,
                // use artifact name & extension
                // Not appropriate here...
                // 2. If an option is specified, use the value of the option
                if (option != null) {
                    try {
                        List<String> outputList = new ArrayList<String>();
                        int optType = option.getValueType();
                        if (optType == IOption.STRING) {
                            outputList.add(outputPrefix + option.getStringValue());
                        } else if (optType == IOption.STRING_LIST || optType == IOption.LIBRARIES
                                || optType == IOption.OBJECTS || optType == IOption.INCLUDE_FILES
                                || optType == IOption.LIBRARY_PATHS || optType == IOption.LIBRARY_FILES
                                || optType == IOption.MACRO_FILES) {
                            List<String> value = (List<String>) option.getValue();
                            outputList = value;
                            ((Tool) tool).filterValues(optType, outputList);
                            // Add outputPrefix to each if necessary
                            if (outputPrefix.length() > 0) {
                                for (int j = 0; j < outputList.size(); j++) {
                                    outputList.set(j, outputPrefix + outputList.get(j));
                                }
                            }
                        }
                        for (int j = 0; j < outputList.size(); j++) {
                            String outputName = outputList.get(j);
                            // try to resolve the build macros in the output
                            // names
                            try {
                                String resolved = null;
                                if (containsSpecialCharacters(sourceLocation.toOSString())) {
                                    resolved = ManagedBuildManager.getBuildMacroProvider().resolveValue(outputName, "",
                                            " ", IBuildMacroProvider.CONTEXT_FILE,
                                            new FileContextData(sourceLocation, null, option, tool));
                                } else {
                                    resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
                                            outputName, "", " ", IBuildMacroProvider.CONTEXT_FILE,
                                            new FileContextData(sourceLocation, null, option, tool));
                                }
                                if ((resolved = resolved.trim()).length() > 0)
                                    outputName = resolved;
                            } catch (BuildMacroException e) {
                                /* JABA is not going to write this code */
                            }
                            IPath outPath = Path.fromOSString(outputName);
                            // If only a file name is specified, add the
                            // relative path of this output directory
                            if (outPath.segmentCount() == 1) {
                                outPath = Path.fromOSString(relativePath + outputList.get(j));
                            }
                            if (primaryOutput) {
                                ruleOutputs.add(j, outPath);
                                enumeratedPrimaryOutputs.add(j, resolvePercent(outPath, sourceLocation));
                            } else {
                                ruleOutputs.add(outPath);
                                enumeratedSecondaryOutputs.add(resolvePercent(outPath, sourceLocation));
                            }
                        }
                    } catch (BuildException ex) {
                        /* JABA is not going to write this code */
                    }
                } else
                // 3. If a nameProvider is specified, call it
                if (nameProvider != null) {
                    IPath[] inPaths = new IPath[1];
                    inPaths[0] = resource.getProjectRelativePath();
                    IPath[] outPaths = null;
                    // try to find out if thisd is JABA name provider
                    try {
                        IManagedOutputNameProviderJaba newNameProvider = (IManagedOutputNameProviderJaba) nameProvider;
                        outPaths = newNameProvider.getOutputNames(project, config, tool, inPaths);
                    } catch (Exception e) {
                        outPaths = nameProvider.getOutputNames(tool, inPaths);
                    }
                    if (outPaths != null) { // MODDED BY JABA ADDED to handle
                        // null as return value
                        usedOutType = type; // MODDED By JABA added to
                        // retun the
                        // output type used to generate the
                        // command line
                        for (int j = 0; j < outPaths.length; j++) {
                            IPath outPath = outPaths[j];
                            String outputName = outPaths[j].toOSString();
                            // try to resolve the build macros in the output
                            // names
                            try {
                                String resolved = null;
                                if (containsSpecialCharacters(sourceLocation.toOSString())) {
                                    resolved = ManagedBuildManager.getBuildMacroProvider().resolveValue(outputName, "",
                                            " ", IBuildMacroProvider.CONTEXT_FILE,
                                            new FileContextData(sourceLocation, null, option, tool));
                                } else {
                                    resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
                                            outputName, "", " ", IBuildMacroProvider.CONTEXT_FILE,
                                            new FileContextData(sourceLocation, null, option, tool));
                                }
                                if ((resolved = resolved.trim()).length() > 0)
                                    outputName = resolved;
                            } catch (BuildMacroException e) {
                                // JABA is not
                                // going to write
                                // this code
                            }
                            // If only a file name is specified, add the
                            // relative path of this output directory
                            if (outPath.segmentCount() == 1) {
                                outPath = Path.fromOSString(relativePath + outPath.toOSString());
                            }
                            if (type.getPrimaryOutput()) {
                                ruleOutputs.add(j, outPath);
                                enumeratedPrimaryOutputs.add(j, resolvePercent(outPath, sourceLocation));
                            } else {
                                ruleOutputs.add(outPath);
                                enumeratedSecondaryOutputs.add(resolvePercent(outPath, sourceLocation));
                            }
                        }
                    } // MODDED BY JABA ADDED
                } else
                // 4. If outputNames is specified, use it
                if (outputNames != null) {
                    for (int j = 0; j < outputNames.length; j++) {
                        String outputName = outputNames[j];
                        try {
                            // try to resolve the build macros in the output
                            // names
                            String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
                                    outputName, "", " ", IBuildMacroProvider.CONTEXT_FILE,
                                    new FileContextData(sourceLocation, null, option, tool));
                            if ((resolved = resolved.trim()).length() > 0)
                                outputName = resolved;
                        } catch (BuildMacroException e) {
                            /* JABA is not going to write this code */
                        }
                        IPath outPath = Path.fromOSString(outputName);
                        // If only a file name is specified, add the relative
                        // path of this output directory
                        if (outPath.segmentCount() == 1) {
                            outPath = Path.fromOSString(relativePath + outPath.toOSString());
                        }
                        if (primaryOutput) {
                            ruleOutputs.add(j, outPath);
                            enumeratedPrimaryOutputs.add(j, resolvePercent(outPath, sourceLocation));
                        } else {
                            ruleOutputs.add(outPath);
                            enumeratedSecondaryOutputs.add(resolvePercent(outPath, sourceLocation));
                        }
                    }
                } else {
                    // 5. Use the name pattern to generate a transformation
                    // macro
                    // so that the source names can be transformed into the
                    // target names
                    // using the built-in string substitution functions of
                    // <code>make</code>.
                    if (multOfType) {
                        // This case is not handled - a nameProvider or
                        // outputNames must be specified
                        // TODO - report error
                    } else {
                        String namePattern = type.getNamePattern();
                        IPath namePatternPath = null;
                        if (namePattern == null || namePattern.length() == 0) {
                            namePattern = relativePath + outputPrefix + IManagedBuilderMakefileGenerator.WILDCARD;
                            if (outExt != null && outExt.length() > 0) {
                                namePattern += DOT + outExt;
                            }
                            namePatternPath = Path.fromOSString(namePattern);
                        } else {
                            if (outputPrefix.length() > 0) {
                                namePattern = outputPrefix + namePattern;
                            }
                            namePatternPath = Path.fromOSString(namePattern);
                            // If only a file name is specified, add the
                            // relative path of this output directory
                            if (namePatternPath.segmentCount() == 1) {
                                namePatternPath = Path.fromOSString(relativePath + namePatternPath.toOSString());
                            }
                        }
                        if (primaryOutput) {
                            ruleOutputs.add(0, namePatternPath);
                            enumeratedPrimaryOutputs.add(0, resolvePercent(namePatternPath, sourceLocation));
                        } else {
                            ruleOutputs.add(namePatternPath);
                            enumeratedSecondaryOutputs.add(resolvePercent(namePatternPath, sourceLocation));
                        }
                    }
                }
            }
        } else {
            // For support of pre-CDT 3.0 integrations.
            // NOTE WELL: This only supports the case of a single "target tool"
            // that consumes exactly all of the object files, $OBJS, produced
            // by other tools in the build and produces a single output.
            // In this case, the output file name is the input file name with
            // the output extension.
            // if (!ignorePrimary) {
            String outPrefix = tool.getOutputPrefix();
            IPath outPath = Path.fromOSString(relativePath + outPrefix + WILDCARD);
            outPath = outPath.addFileExtension(outExt);
            ruleOutputs.add(0, outPath);
            enumeratedPrimaryOutputs.add(0, resolvePercent(outPath, sourceLocation));
            // }
        }
    }

    /**
     * If the path contains a %, returns the path resolved using the resource name
     */
    protected IPath resolvePercent(IPath outPath, IPath sourceLocation) {
        // Get the input file name
        String fileName = sourceLocation.removeFileExtension().lastSegment();
        // Replace the % with the file name
        String outName = outPath.toOSString().replace("%", fileName);
        IPath result = Path.fromOSString(outName);
        return DOT_SLASH_PATH.isPrefixOf(outPath) ? DOT_SLASH_PATH.append(result) : result;
    }

    /**
     * Returns the dependency <code>IPath</code>s for this invocation of the tool
     * with the specified source file
     *
     * @param depGen
     *            the dependency calculator
     * @param tool
     *            tool used to build the source file
     * @param relativePath
     *            build output directory relative path of the current output
     *            directory
     * @param resource
     *            source file to scan for dependencies
     * @return Vector of IPaths that are relative to the build directory
     */
    protected IPath[] oldCalculateDependenciesForSource(IManagedDependencyGenerator depGen, ITool tool,
            String relativePath, IResource resource) {
        Vector<IPath> deps = new Vector<IPath>();
        int type = depGen.getCalculatorType();
        switch (type) {
        case IManagedDependencyGeneratorType.TYPE_INDEXER:
        case IManagedDependencyGeneratorType.TYPE_EXTERNAL:
            IResource[] res = depGen.findDependencies(resource, project);
            if (res != null) {
                for (IResource re : res) {
                    IPath dep = null;
                    if (re != null) {
                        IPath addlPath = re.getLocation();
                        if (addlPath != null) {
                            dep = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), addlPath);
                        }
                    }
                    if (dep != null) {
                        deps.add(dep);
                    }
                }
            }
            break;
        case IManagedDependencyGeneratorType.TYPE_NODEPS:
        default:
            break;
        }
        return deps.toArray(new IPath[deps.size()]);
    }

    /**
     * Returns the dependency <code>IPath</code>s relative to the build directory
     *
     * @param depCalculator
     *            the dependency calculator
     * @return IPath[] that are relative to the build directory
     */
    protected IPath[] calculateDependenciesForSource(IManagedDependencyCalculator depCalculator) {
        IPath[] addlDeps = depCalculator.getDependencies();
        if (addlDeps != null) {
            for (int i = 0; i < addlDeps.length; i++) {
                if (!addlDeps[i].isAbsolute()) {
                    // Convert from project relative to build directory relative
                    IPath absolutePath = project.getLocation().append(addlDeps[i]);
                    addlDeps[i] = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), absolutePath);
                }
            }
        }
        return addlDeps;
    }

    /*************************************************************************
     * M A K E F I L E G E N E R A T I O N C O M M O N M E T H O D S
     ************************************************************************/
    /**
     * Generates a source macro name from a file extension
     */
    public StringBuffer getSourceMacroName(String extensionName) {
        StringBuffer macroName = new StringBuffer();
        // We need to handle case sensitivity in file extensions (e.g. .c vs
        // .C), so if the
        // extension was already upper case, tack on an "UPPER_" to the macro
        // name.
        // In theory this means there could be a conflict if you had for
        // example,
        // extensions .c_upper, and .C, but realistically speaking the chances
        // of this are
        // practically nil so it doesn't seem worth the hassle of generating a
        // truly
        // unique name.
        if (extensionName.equals(extensionName.toUpperCase())) {
            macroName.append(extensionName.toUpperCase()).append("_UPPER");
        } else {
            // lower case... no need for "UPPER_"
            macroName.append(extensionName.toUpperCase());
        }
        macroName.append("_SRCS");
        return macroName;
    }

    /**
     * Generates a generated dependency file macro name from a file extension
     */
    public StringBuffer getDepMacroName(String extensionName) {
        StringBuffer macroName = new StringBuffer();
        // We need to handle case sensitivity in file extensions (e.g. .c vs
        // .C), so if the
        // extension was already upper case, tack on an "UPPER_" to the macro
        // name.
        // In theory this means there could be a conflict if you had for
        // example,
        // extensions .c_upper, and .C, but realistically speaking the chances
        // of this are
        // practically nil so it doesn't seem worth the hassle of generating a
        // truly
        // unique name.
        if (extensionName.equals(extensionName.toUpperCase())) {
            macroName.append(extensionName.toUpperCase()).append("_UPPER");
        } else {
            // lower case... no need for "UPPER_"
            macroName.append(extensionName.toUpperCase());
        }
        macroName.append("_DEPS");
        return macroName;
    }

    /**
     * Answers all of the output extensions that the target of the build has tools
     * defined to work on.
     *
     * @return a <code>Set</code> containing all of the output extensions
     */
    public Set<String> getOutputExtensions(ToolInfoHolder h) {
        if (h.outputExtensionsSet == null) {
            // The set of output extensions which will be produced by this tool.
            // It is presumed that this set is not very large (likely < 10) so
            // a HashSet should provide good performance.
            h.outputExtensionsSet = new HashSet<>();
            // For each tool for the target, lookup the kinds of sources it
            // outputs
            // and add that to our list of output extensions.
            for (ITool tool : h.buildTools) {
                String[] outputs = tool.getAllOutputExtensions();
                if (outputs != null) {
                    h.outputExtensionsSet.addAll(Arrays.asList(outputs));
                }
            }
        }
        return h.outputExtensionsSet;
    }

    /**
     * This method postprocesses a .d file created by a build. It's main job is to
     * add dummy targets for the header files dependencies. This prevents make from
     * aborting the build if the header file does not exist. A secondary job is to
     * work in tandem with the "echo" command that is used by some tool-chains in
     * order to get the "targets" part of the dependency rule correct. This method
     * adds a comment to the beginning of the dependency file which it checks for to
     * determine if this dependency file has already been updated.
     *
     * @return a <code>true</code> if the dependency file is modified
     */
    static public boolean populateDummyTargets(IConfiguration cfg, IFile makefile, boolean force)
            throws CoreException, IOException {
        return populateDummyTargets(cfg.getRootFolderInfo(), makefile, force);
    }

    static public boolean populateDummyTargets(IResourceInfo rcInfo, IFile makefile, boolean force)
            throws CoreException, IOException {
        if (makefile == null || !makefile.exists())
            return false;
        // Get the contents of the dependency file
        InputStream contentStream = makefile.getContents(false);
        StringBuffer inBuffer = null;
        // JABA made sure thgere are no emory leaks
        try (Reader in = new InputStreamReader(contentStream);) {
            int chunkSize = contentStream.available();
            inBuffer = new StringBuffer(chunkSize);
            char[] readBuffer = new char[chunkSize];
            int n = in.read(readBuffer);
            while (n > 0) {
                inBuffer.append(readBuffer);
                n = in.read(readBuffer);
            }
            contentStream.close();
            in.close();
        }
        // The rest of this operation is equally expensive, so
        // if we are doing an incremental build, only update the
        // files that do not have a comment
        String inBufferString = inBuffer.toString();
        if (!force && inBufferString.startsWith(COMMENT_SYMBOL)) {
            return false;
        }
        // Try to determine if this file already has dummy targets defined.
        // If so, we will only add the comment.
        String[] bufferLines = inBufferString.split("[\\r\\n]");
        for (String bufferLine : bufferLines) {
            if (bufferLine.endsWith(":")) {
                StringBuffer outBuffer = addDefaultHeader();
                outBuffer.append(inBuffer);
                save(outBuffer, makefile);
                return true;
            }
        }
        // Reconstruct the buffer tokens into useful chunks of dependency
        // information
        Vector<String> bufferTokens = new Vector<String>(Arrays.asList(inBufferString.split("\\s")));
        Vector<String> deps = new Vector<String>(bufferTokens.size());
        Iterator<String> tokenIter = bufferTokens.iterator();
        while (tokenIter.hasNext()) {
            String token = tokenIter.next();
            if (token.lastIndexOf("\\") == token.length() - 1 && token.length() > 1) {
                // This is escaped so keep adding to the token until we find the
                // end
                while (tokenIter.hasNext()) {
                    String nextToken = tokenIter.next();
                    token += WHITESPACE + nextToken;
                    if (!nextToken.endsWith("\\")) {
                        break;
                    }
                }
            }
            deps.add(token);
        }
        deps.trimToSize();
        // Now find the header file dependencies and make dummy targets for them
        boolean save = false;
        StringBuffer outBuffer = null;
        // If we are doing an incremental build, only update the files that do
        // not have a comment
        String firstToken;
        try {
            firstToken = deps.get(0);
        } catch (ArrayIndexOutOfBoundsException e) {
            // This makes no sense so bail
            return false;
        }
        // Put the generated comments in the output buffer
        if (!firstToken.startsWith(COMMENT_SYMBOL)) {
            outBuffer = addDefaultHeader();
        } else {
            outBuffer = new StringBuffer();
        }
        // Some echo implementations misbehave and put the -n and newline in the
        // output
        if (firstToken.startsWith("-n")) {
            // Now let's parse:
            // Win32 outputs -n '<path>/<file>.d <path>/'
            // POSIX outputs -n <path>/<file>.d <path>/
            // Get the dep file name
            String secondToken;
            try {
                secondToken = deps.get(1);
            } catch (ArrayIndexOutOfBoundsException e) {
                secondToken = "";
            }
            if (secondToken.startsWith("'")) {
                // This is the Win32 implementation of echo (MinGW without MSYS)
                outBuffer.append(secondToken.substring(1)).append(WHITESPACE);
            } else {
                outBuffer.append(secondToken).append(WHITESPACE);
            }
            // The relative path to the build goal comes next
            String thirdToken;
            try {
                thirdToken = deps.get(2);
            } catch (ArrayIndexOutOfBoundsException e) {
                thirdToken = "";
            }
            int lastIndex = thirdToken.lastIndexOf("'");
            if (lastIndex != -1) {
                if (lastIndex == 0) {
                    outBuffer.append(WHITESPACE);
                } else {
                    outBuffer.append(thirdToken.substring(0, lastIndex - 1));
                }
            } else {
                outBuffer.append(thirdToken);
            }
            // Followed by the target output by the compiler plus ':'
            // If we see any empty tokens here, assume they are the result of
            // a line feed output by "echo" and skip them
            String fourthToken;
            int nToken = 3;
            try {
                do {
                    fourthToken = deps.get(nToken++);
                } while (fourthToken.length() == 0);
            } catch (ArrayIndexOutOfBoundsException e) {
                fourthToken = "";
            }
            outBuffer.append(fourthToken).append(WHITESPACE);
            // Followed by the actual dependencies
            try {
                for (String nextElement : deps) {
                    if (nextElement.endsWith("\\")) {
                        outBuffer.append(nextElement).append(NEWLINE).append(WHITESPACE);
                    } else {
                        outBuffer.append(nextElement).append(WHITESPACE);
                    }
                }
            } catch (IndexOutOfBoundsException e) {
                /* JABA is not going to write this code */
            }
        } else {
            outBuffer.append(inBuffer);
        }
        outBuffer.append(NEWLINE);
        save = true;
        IFolderInfo fo = null;
        if (rcInfo instanceof IFolderInfo) {
            fo = (IFolderInfo) rcInfo;
        } else {
            IConfiguration c = rcInfo.getParent();
            fo = (IFolderInfo) c.getResourceInfo(rcInfo.getPath().removeLastSegments(1), false);
        }
        // Dummy targets to add to the makefile
        for (String dummy : deps) {
            IPath dep = new Path(dummy);
            String extension = dep.getFileExtension();
            if (fo.isHeaderFile(extension)) {
                /*
                 * The formatting here is <dummy_target>:
                 */
                outBuffer.append(dummy).append(COLON).append(NEWLINE).append(NEWLINE);
            }
        }
        // Write them out to the makefile
        if (save) {
            save(outBuffer, makefile);
            return true;
        }
        return false;
    }

    /**
     * prepend all instanced of '\' or '"' with a backslash
     *
     * @return resulting string
     */
    static public String escapedEcho(String string) {
        String escapedString = string.replace("'", "'\"'\"'");
        return ECHO + WHITESPACE + SINGLE_QUOTE + escapedString + SINGLE_QUOTE + NEWLINE;
    }

    static public String ECHO_BLANK_LINE = ECHO + WHITESPACE + SINGLE_QUOTE + WHITESPACE + SINGLE_QUOTE + NEWLINE;

    /**
     * Outputs a comment formatted as follows: ##### ....... ##### # <Comment
     * message> ##### ....... #####
     */
    static protected StringBuffer addDefaultHeader() {
        StringBuffer buffer = new StringBuffer();
        outputCommentLine(buffer);
        buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(HEADER))
                .append(NEWLINE);
        outputCommentLine(buffer);
        buffer.append(NEWLINE);
        return buffer;
    }

    /**
     * Put COLS_PER_LINE comment charaters in the argument.
     */
    static protected void outputCommentLine(StringBuffer buffer) {
        for (int i = 0; i < COLS_PER_LINE; i++) {
            buffer.append(COMMENT_SYMBOL);
        }
        buffer.append(NEWLINE);
    }

    static public boolean containsSpecialCharacters(String path) {
        return path.matches(".*(\\s|[\\{\\}\\(\\)\\$\\@%=;]).*");
    }

    /**
     * Answers the argument with all whitespaces replaced with an escape sequence.
     */
    static public String escapeWhitespaces(String path) {
        // Escape the spaces in the path/filename if it has any
        String[] segments = path.split("\\s");
        if (segments.length > 1) {
            StringBuffer escapedPath = new StringBuffer();
            for (int index = 0; index < segments.length; ++index) {
                escapedPath.append(segments[index]);
                if (index + 1 < segments.length) {
                    escapedPath.append("\\ ");
                }
            }
            return escapedPath.toString().trim();
        }
        return path;
    }

    /**
     * Adds a macro addition prefix to a map of macro names to entries. Entry
     * prefixes look like: C_SRCS += \ ${addprefix $(ROOT)/, \
     */
    protected void addMacroAdditionPrefix(LinkedHashMap<String, String> map, String macroName, String relativePath,
            boolean addPrefix) {
        // there is no entry in the map, so create a buffer for this macro
        StringBuffer tempBuffer = new StringBuffer();
        tempBuffer.append(macroName).append(WHITESPACE).append(MACRO_ADDITION_PREFIX_SUFFIX);
        if (addPrefix) {
            tempBuffer.append(new Path(MACRO_ADDITION_ADDPREFIX_HEADER).append(relativePath)
                    .append(MACRO_ADDITION_ADDPREFIX_SUFFIX).toOSString());
        }
        // have to store the buffer in String form as StringBuffer is not a
        // sublcass of Object
        map.put(macroName, tempBuffer.toString());
    }

    /**
     * Adds a file to an entry in a map of macro names to entries. File additions
     * look like: example.c, \
     */
    protected void addMacroAdditionFile(HashMap<String, String> map, String macroName, String filename) {
        StringBuffer buffer = new StringBuffer();
        buffer.append(map.get(macroName));
        filename = escapeWhitespaces(filename);
        buffer.append(filename).append(WHITESPACE).append(LINEBREAK);
        map.put(macroName, buffer.toString());
    }

    /**
     * Adds a file to an entry in a map of macro names to entries. File additions
     * look like: example.c, \
     */
    protected void addMacroAdditionFile(HashMap<String, String> map, String macroName, String relativePath,
            IPath sourceLocation, boolean generatedSource) {
        // Add the source file path to the makefile line that adds source files
        // to the build variable
        String srcName;
        IPath projectLocation = getPathForResource(project);
        IPath dirLocation = projectLocation;
        if (generatedSource) {
            dirLocation = dirLocation.append(getBuildWorkingDir());
        }
        if (dirLocation.isPrefixOf(sourceLocation)) {
            IPath srcPath = sourceLocation.removeFirstSegments(dirLocation.segmentCount()).setDevice(null);
            if (generatedSource) {
                srcName = DOT_SLASH_PATH.append(srcPath).toOSString();
            } else {
                srcName = ROOT + FILE_SEPARATOR + srcPath.toOSString();
            }
        } else {
            if (generatedSource && !sourceLocation.isAbsolute()) {
                srcName = DOT_SLASH_PATH.append(relativePath).append(sourceLocation.lastSegment()).toOSString();
            } else {
                // TODO: Should we use relative paths when possible (e.g., see
                // MbsMacroSupplier.calculateRelPath)
                srcName = sourceLocation.toOSString();
            }
        }
        addMacroAdditionFile(map, macroName, srcName);
    }

    /**
     * Adds file(s) to an entry in a map of macro names to entries. File additions
     * look like: example.c, \
     */
    public void addMacroAdditionFiles(HashMap<String, String> map, String macroName, Vector<String> filenames) {
        StringBuffer buffer = new StringBuffer();
        buffer.append(map.get(macroName));
        for (int i = 0; i < filenames.size(); i++) {
            String filename = filenames.get(i);
            if (filename.length() > 0) {
                filename = ensurePathIsGNUMakeTargetRuleCompatibleSyntax(filename);
                buffer.append(new Path(filename).toOSString()).append(WHITESPACE).append(LINEBREAK);
            }
        }
        // re-insert string in the map
        map.put(macroName, buffer.toString());
    }

    /**
     * Write all macro addition entries in a map to the buffer
     */
    protected StringBuffer writeAdditionMacros(LinkedHashMap<String, String> map) {
        StringBuffer buffer = new StringBuffer();
        // Add the comment
        buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(MOD_VARS))
                .append(NEWLINE);
        for (String macroString : map.values()) {
            // Check if we added any files to the rule
            // Currently, we do this by comparing the end of the rule buffer to
            // MACRO_ADDITION_PREFIX_SUFFIX
            if (!(macroString.endsWith(MACRO_ADDITION_PREFIX_SUFFIX))) {
                StringBuffer currentBuffer = new StringBuffer();
                // Remove the final "/"
                if (macroString.endsWith(LINEBREAK)) {
                    macroString = macroString.substring(0, (macroString.length() - 2)) + NEWLINE;
                }
                currentBuffer.append(macroString);
                currentBuffer.append(NEWLINE);
                // append the contents of the buffer to the master buffer for
                // the whole file
                buffer.append(currentBuffer);
            }
        }
        return buffer.append(NEWLINE);
    }

    /**
     * Write all macro addition entries in a map to the buffer
     */
    protected StringBuffer writeTopAdditionMacros(List<String> varList, HashMap<String, String> varMap) {
        StringBuffer buffer = new StringBuffer();
        // Add the comment
        buffer.append(COMMENT_SYMBOL).append(WHITESPACE).append(ManagedMakeMessages.getResourceString(MOD_VARS))
                .append(NEWLINE);
        for (int i = 0; i < varList.size(); i++) {
            String addition = varMap.get(varList.get(i));
            StringBuffer currentBuffer = new StringBuffer();
            currentBuffer.append(addition);
            currentBuffer.append(NEWLINE);
            // append the contents of the buffer to the master buffer for the
            // whole file
            buffer.append(currentBuffer);
        }
        return buffer.append(NEWLINE);
    }

    /**
     * Calculates the inputs and outputs for tools that will be generated in the top
     * makefile. This information is used by the top level makefile generation
     * methods.
     */
    protected void calculateToolInputsOutputs() {
        toolInfos.accept(new IPathSettingsContainerVisitor() {
            @Override
            public boolean visit(PathSettingsContainer container) {
                ToolInfoHolder h = (ToolInfoHolder) container.getValue();
                ITool[] buildTools = h.buildTools;
                ArduinoManagedBuildGnuToolInfo[] gnuToolInfos = h.gnuToolInfos;
                // We are "done" when the information for all tools has been
                // calculated,
                // or we are not making any progress
                boolean done = false;
                boolean lastChance = false;
                int[] doneState = new int[buildTools.length];
                // Identify the target tool
                ITool targetTool = config.calculateTargetTool();
                // if (targetTool == null) {
                // targetTool = info.getToolFromOutputExtension(buildTargetExt);
                // }
                // Initialize the tool info array and the done state
                if (buildTools.length != 0 && buildTools[0].getCustomBuildStep())
                    return true;
                for (int i = 0; i < buildTools.length; i++) {
                    if ((buildTools[i] == targetTool)) {
                        String ext = config.getArtifactExtension();
                        // try to resolve the build macros in the artifact
                        // extension
                        try {
                            ext = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(ext, "", " ",
                                    IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
                        } catch (BuildMacroException e) {
                            /* JABA is not going to write this code */
                        }
                        String name = config.getArtifactName();
                        // try to resolve the build macros in the artifact name
                        try {
                            String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
                                    name, "", " ", IBuildMacroProvider.CONTEXT_CONFIGURATION, config);
                            if ((resolved = resolved.trim()).length() > 0)
                                name = resolved;
                        } catch (BuildMacroException e) {
                            /* JABA is not going to write this code */
                        }
                        gnuToolInfos[i] = new ArduinoManagedBuildGnuToolInfo(project, buildTools[i], true, name, ext);
                    } else {
                        gnuToolInfos[i] = new ArduinoManagedBuildGnuToolInfo(project, buildTools[i], false, null, null);
                    }
                    doneState[i] = 0;
                }
                // Initialize the build output variable to file additions map
                LinkedHashMap<String, String> map = getTopBuildOutputVars();
                Set<Entry<String, List<IPath>>> set = buildOutVars.entrySet();
                for (Entry<String, List<IPath>> entry : set) {
                    String macroName = entry.getKey();
                    // for projects with specific setting on folders/files do
                    // not clear the macro value on subsequent passes
                    if (!map.containsKey(macroName)) {
                        addMacroAdditionPrefix(map, macroName, "", false);
                    }
                }
                // Set of input extensions for which macros have been created so
                // far
                HashSet<String> handledDepsInputExtensions = new HashSet<>();
                HashSet<String> handledOutsInputExtensions = new HashSet<>();
                while (!done) {
                    int[] testState = new int[doneState.length];
                    for (int i = 0; i < testState.length; i++)
                        testState[i] = 0;
                    // Calculate inputs
                    for (int i = 0; i < gnuToolInfos.length; i++) {
                        if (gnuToolInfos[i].areInputsCalculated()) {
                            testState[i]++;
                        } else {
                            if (gnuToolInfos[i].calculateInputs(ArduinoGnuMakefileGenerator.this, config,
                                    projectResources, h, lastChance)) {
                                testState[i]++;
                            }
                        }
                    }
                    // Calculate dependencies
                    for (int i = 0; i < gnuToolInfos.length; i++) {
                        if (gnuToolInfos[i].areDependenciesCalculated()) {
                            testState[i]++;
                        } else {
                            if (gnuToolInfos[i].calculateDependencies(ArduinoGnuMakefileGenerator.this, config,
                                    handledDepsInputExtensions, h, lastChance)) {
                                testState[i]++;
                            }
                        }
                    }
                    // Calculate outputs
                    for (int i = 0; i < gnuToolInfos.length; i++) {
                        if (gnuToolInfos[i].areOutputsCalculated()) {
                            testState[i]++;
                        } else {
                            if (gnuToolInfos[i].calculateOutputs(ArduinoGnuMakefileGenerator.this, config,
                                    handledOutsInputExtensions, lastChance)) {
                                testState[i]++;
                            }
                        }
                    }
                    // Are all calculated? If so, done.
                    done = true;
                    for (int element : testState) {
                        if (element != 3) {
                            done = false;
                            break;
                        }
                    }
                    // Test our "done" state vs. the previous "done" state.
                    // If we have made no progress, give it a "last chance" and
                    // then quit
                    if (!done) {
                        done = true;
                        for (int i = 0; i < testState.length; i++) {
                            if (testState[i] != doneState[i]) {
                                done = false;
                                break;
                            }
                        }
                    }
                    if (done) {
                        if (!lastChance) {
                            lastChance = true;
                            done = false;
                        }
                    }
                    if (!done) {
                        doneState = testState;
                    }
                }
                return true;
            }
        });
    }

    /**
     * Returns the (String) list of files associated with the build variable
     *
     * @param variable
     *            the variable name
     * @param locationType
     *            the format in which we want the filenames returned
     * @param directory
     *            project relative directory path used with locationType ==
     *            DIRECTORY_RELATIVE
     * @param getAll
     *            only return the list if all tools that are going to contrubute to
     *            this variable have done so.
     * @return List
     */
    public List<String> getBuildVariableList(ToolInfoHolder h, String variable, int locationType, IPath directory,
            boolean getAll) {
        ArduinoManagedBuildGnuToolInfo[] gnuToolInfos = h.gnuToolInfos;
        boolean done = true;
        for (int i = 0; i < gnuToolInfos.length; i++) {
            if (!gnuToolInfos[i].areOutputVariablesCalculated()) {
                done = false;
            }
        }
        if (!done && getAll)
            return null;
        List<IPath> list = buildSrcVars.get(variable);
        if (list == null) {
            list = buildOutVars.get(variable);
        }
        List<String> fileList = null;
        if (list != null) {
            // Convert the items in the list to the location-type wanted by the
            // caller,
            // and to a string list
            IPath dirLocation = null;
            if (locationType != ABSOLUTE) {
                dirLocation = project.getLocation();
                if (locationType == PROJECT_SUBDIR_RELATIVE) {
                    dirLocation = dirLocation.append(directory);
                }
            }
            for (int i = 0; i < list.size(); i++) {
                IPath path = list.get(i);
                if (locationType != ABSOLUTE) {
                    if (dirLocation != null && dirLocation.isPrefixOf(path)) {
                        path = path.removeFirstSegments(dirLocation.segmentCount()).setDevice(null);
                    }
                }
                if (fileList == null) {
                    fileList = new Vector<String>();
                }
                fileList.add(path.toString());
            }
        }
        return fileList;
    }

    /**
     * Returns the map of build variables to list of files
     *
     * @return HashMap
     */
    public HashMap<String, List<IPath>> getBuildOutputVars() {
        return buildOutVars;
    }

    /**
     * Returns the map of build variables used in the top makefile to list of files
     *
     * @return HashMap
     */
    public LinkedHashMap<String, String> getTopBuildOutputVars() {
        return topBuildOutVars;
    }

    /**
     * Returns the list of known build rules. This keeps me from generating
     * duplicate rules for known file extensions.
     *
     * @return List
     */
    protected Vector<String> getRuleList() {
        if (ruleList == null) {
            ruleList = new Vector<String>();
        }
        return ruleList;
    }

    /**
     * Returns the list of known dependency lines. This keeps me from generating
     * duplicate lines.
     *
     * @return List
     */
    protected Vector<String> getDepLineList() {
        if (depLineList == null) {
            depLineList = new Vector<String>();
        }
        return depLineList;
    }

    /**
     * Returns the list of known dependency file generation lines. This keeps me
     * from generating duplicate lines.
     *
     * @return List
     */
    protected Vector<String> getDepRuleList() {
        if (depRuleList == null) {
            depRuleList = new Vector<String>();
        }
        return depRuleList;
    }

    /*************************************************************************
     * R E S O U R C E V I S I T O R M E T H O D S
     ************************************************************************/
    /**
     * Adds the container of the argument to the list of folders in the project that
     * contribute source files to the build. The resource visitor has already
     * established that the build model knows how to build the files. It has also
     * checked that the resource is not generated as part of the build.
     */
    protected void appendBuildSubdirectory(IResource resource) {
        IContainer container = resource.getParent();
        // Only add the container once
        if (!getSubdirList().contains(container))
            getSubdirList().add(container);
    }

    /**
     * Adds the container of the argument to a list of subdirectories that are to be
     * deleted. As a result, the directories that are generated for the output
     * should be removed as well.
     */
    protected void appendDeletedSubdirectory(IContainer container) {
        // No point in adding a folder if the parent is already there
        IContainer parent = container.getParent();
        if (!getDeletedDirList().contains(container) && !getDeletedDirList().contains(parent)) {
            getDeletedDirList().add(container);
        }
    }

    /**
     * If a file is removed from a source folder (either because of a delete or move
     * action on the part of the user), the makefilegenerator has to remove the
     * dependency makefile along with the old build goal
     */
    protected void appendDeletedFile(IResource resource) {
        // Cache this for now
        getDeletedFileList().add(resource);
    }

    /**
     * Adds the container of the argument to a list of subdirectories that are part
     * of an incremental rebuild of the project. The makefile fragments for these
     * directories will be regenerated as a result of the build.
     */
    protected void appendModifiedSubdirectory(IResource resource) {
        IContainer container = resource.getParent();
        if (!getModifiedList().contains(container)) {
            getModifiedList().add(container);
        }
    }

    /*************************************************************************
     * O T H E R M E T H O D S
     ************************************************************************/
    protected void cancel(String message) {
        if (monitor != null && !monitor.isCanceled()) {
            throw new OperationCanceledException(message);
        }
    }

    /**
     * Check whether the build has been cancelled. Cancellation requests propagated
     * to the caller by throwing <code>OperationCanceledException</code>.
     *
     * @see org.eclipse.core.runtime.OperationCanceledException#OperationCanceledException()
     */
    protected void checkCancel() {
        if (monitor != null && monitor.isCanceled()) {
            throw new OperationCanceledException();
        }
    }

    /**
     * Return or create the folder needed for the build output. If we are creating
     * the folder, set the derived bit to true so the CM system ignores the
     * contents. If the resource exists, respect the existing derived setting.
     */
    private IPath createDirectory(String dirName) throws CoreException {
        // Create or get the handle for the build directory
        IFolder folder = project.getFolder(dirName);
        if (!folder.exists()) {
            // Make sure that parent folders exist
            IPath parentPath = (new Path(dirName)).removeLastSegments(1);
            // Assume that the parent exists if the path is empty
            if (!parentPath.isEmpty()) {
                IFolder parent = project.getFolder(parentPath);
                if (!parent.exists()) {
                    createDirectory(parentPath.toString());
                }
            }
            // Now make the requested folder
            try {
                folder.create(true, true, null);
            } catch (CoreException e) {
                if (e.getStatus().getCode() == IResourceStatus.PATH_OCCUPIED)
                    folder.refreshLocal(IResource.DEPTH_ZERO, null);
                else
                    throw e;
            }
            // Make sure the folder is marked as derived so it is not added to
            // CM
            if (!folder.isDerived()) {
                folder.setDerived(true, null);
            }
        }
        return folder.getFullPath();
    }

    /**
     * Return or create the makefile needed for the build. If we are creating the
     * resource, set the derived bit to true so the CM system ignores the contents.
     * If the resource exists, respect the existing derived setting.
     */
    private IFile createFile(IPath makefilePath) throws CoreException {
        // Create or get the handle for the makefile
        IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
        IFile newFile = root.getFileForLocation(makefilePath);
        if (newFile == null) {
            newFile = root.getFile(makefilePath);
        }
        // Create the file if it does not exist
        ByteArrayInputStream contents = new ByteArrayInputStream(new byte[0]);
        try {
            newFile.create(contents, false, new SubProgressMonitor(monitor, 1));
            // Make sure the new file is marked as derived
            if (!newFile.isDerived()) {
                newFile.setDerived(true, null);
            }
        } catch (CoreException e) {
            // If the file already existed locally, just refresh to get contents
            if (e.getStatus().getCode() == IResourceStatus.PATH_OCCUPIED)
                newFile.refreshLocal(IResource.DEPTH_ZERO, null);
            else
                throw e;
        }
        return newFile;
    }

    private void deleteBuildTarget(IResource deletedFile) {
        // Get the project relative path of the file
        String fileName = getFileName(deletedFile);
        String srcExtension = deletedFile.getFileExtension();
        IPath folderPath = inFullPathFromOutFullPath(deletedFile.getFullPath().removeLastSegments(1));
        if (folderPath != null) {
            folderPath = folderPath.removeFirstSegments(1);
        } else {
            folderPath = new Path("");
        }
        IResourceInfo rcInfo = config.getResourceInfo(folderPath, false);
        if (rcInfo instanceof IFileInfo) {
            rcInfo = config.getResourceInfo(folderPath.removeLastSegments(1), false);
        }
        String targetExtension = ((IFolderInfo) rcInfo).getOutputExtension(srcExtension);
        if (!targetExtension.isEmpty())
            fileName += DOT + targetExtension;
        IPath projectRelativePath = deletedFile.getProjectRelativePath().removeLastSegments(1);
        IPath targetFilePath = getBuildWorkingDir().append(projectRelativePath).append(fileName);
        IResource depFile = project.findMember(targetFilePath);
        if (depFile != null && depFile.exists()) {
            try {
                depFile.delete(true, new SubProgressMonitor(monitor, 1));
            } catch (CoreException e) {
                // This had better be allowed during a build
            }
        }
    }

    private IPath inFullPathFromOutFullPath(IPath path) {
        IPath inPath = null;
        if (topBuildDir.isPrefixOf(path)) {
            inPath = path.removeFirstSegments(topBuildDir.segmentCount());
            inPath = project.getFullPath().append(path);
        }
        return inPath;
    }

    private void deleteDepFile(IResource deletedFile) {
        // Calculate the top build directory relative paths of the dependency
        // files
        IPath[] depFilePaths = null;
        ITool tool = null;
        IManagedDependencyGeneratorType depType = null;
        String sourceExtension = deletedFile.getFileExtension();
        IPath folderPath = inFullPathFromOutFullPath(deletedFile.getFullPath().removeLastSegments(1));
        if (folderPath != null) {
            folderPath = folderPath.removeFirstSegments(1);
        } else {
            folderPath = new Path("");
            ToolInfoHolder h = getToolInfo(folderPath);
            ITool[] tools = h.buildTools;
            for (int index = 0; index < tools.length; ++index) {
                if (tools[index].buildsFileType(sourceExtension)) {
                    tool = tools[index];
                    depType = tool.getDependencyGeneratorForExtension(sourceExtension);
                    break;
                }
            }
            if (depType != null) {
                int calcType = depType.getCalculatorType();
                if (calcType == IManagedDependencyGeneratorType.TYPE_COMMAND) {
                    depFilePaths = new IPath[1];
                    IPath absolutePath = deletedFile.getLocation();
                    depFilePaths[0] = ManagedBuildManager.calculateRelativePath(getTopBuildDir(), absolutePath);
                    depFilePaths[0] = depFilePaths[0].removeFileExtension().addFileExtension(DEP_EXT);
                } else if (calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS
                        || calcType == IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS) {
                    IManagedDependencyGenerator2 depGen = (IManagedDependencyGenerator2) depType;
                    IManagedDependencyInfo depInfo = depGen.getDependencySourceInfo(
                            deletedFile.getProjectRelativePath(), deletedFile, config, tool, getBuildWorkingDir());
                    if (depInfo != null) {
                        if (calcType == IManagedDependencyGeneratorType.TYPE_BUILD_COMMANDS) {
                            IManagedDependencyCommands depCommands = (IManagedDependencyCommands) depInfo;
                            depFilePaths = depCommands.getDependencyFiles();
                        } else if (calcType == IManagedDependencyGeneratorType.TYPE_PREBUILD_COMMANDS) {
                            IManagedDependencyPreBuild depPreBuild = (IManagedDependencyPreBuild) depInfo;
                            depFilePaths = depPreBuild.getDependencyFiles();
                        }
                    }
                }
            }
            // Delete the files if they exist
            if (depFilePaths != null) {
                for (IPath dfp : depFilePaths) {
                    IPath depFilePath = getBuildWorkingDir().append(dfp);
                    IResource depFile = project.findMember(depFilePath);
                    if (depFile != null && depFile.exists()) {
                        try {
                            depFile.delete(true, new SubProgressMonitor(monitor, 1));
                        } catch (CoreException e) {
                            // This had better be allowed during a build
                        }
                    }
                }
            }
        }
    }

    /**
     * Returns the current build configuration.
     *
     * @return String
     * @since 8.0
     */
    protected IConfiguration getConfig() {
        return config;
    }

    /**
     * Returns the build target extension.
     *
     * @return String
     * @since 8.0
     */
    protected String getBuildTargetExt() {
        return buildTargetExt;
    }

    /**
     * Returns the build target name.
     *
     * @return String
     * @since 8.0
     */
    protected String getBuildTargetName() {
        return buildTargetName;
    }

    /**
     * @return Returns the deletedDirList.
     */
    private Vector<IResource> getDeletedDirList() {
        if (deletedDirList == null) {
            deletedDirList = new Vector<IResource>();
        }
        return deletedDirList;
    }

    private Vector<IResource> getDeletedFileList() {
        if (deletedFileList == null) {
            deletedFileList = new Vector<IResource>();
        }
        return deletedFileList;
    }

    private List<IPath> getDependencyMakefiles(ToolInfoHolder h) {
        if (h.dependencyMakefiles == null) {
            h.dependencyMakefiles = new ArrayList<IPath>();
        }
        return h.dependencyMakefiles;
    }

    /**
     * Strips off the file extension from the argument and returns the name
     * component in a <code>String</code>
     */
    private String getFileName(IResource file) {
        String answer = "";
        String lastSegment = file.getName();
        int extensionSeparator = lastSegment.lastIndexOf(DOT);
        if (extensionSeparator != -1) {
            answer = lastSegment.substring(0, extensionSeparator);
        }
        return answer;
    }

    /**
     * Answers a Vector containing a list of directories that are invalid for the
     * build for some reason. At the moment, the only reason a directory would not
     * be considered for the build is if it contains a space in the relative path
     * from the project root.
     *
     * @return a a list of directories that are invalid for the build
     */
    private Vector<IResource> getInvalidDirList() {
        if (invalidDirList == null) {
            invalidDirList = new Vector<IResource>();
        }
        return invalidDirList;
    }

    /**
     * @return Collection of Containers which contain modified source files
     */
    private Collection<IContainer> getModifiedList() {
        if (modifiedList == null)
            modifiedList = new LinkedHashSet<IContainer>();
        return modifiedList;
    }

    /**
     * @return Collection of subdirectories (IContainers) contributing source code
     *         to the build
     */
    private Collection<IContainer> getSubdirList() {
        if (subdirList == null)
            subdirList = new LinkedHashSet<IContainer>();
        return subdirList;
    }

    private void removeGeneratedDirectory(IContainer subDir) {
        try {
            // The source directory isn't empty
            if (subDir.exists() && subDir.members().length > 0)
                return;
        } catch (CoreException e) {
            // The resource doesn't exist so we should delete the output folder
        }
        // Figure out what the generated directory name is and delete it
        IPath moduleRelativePath = subDir.getProjectRelativePath();
        IPath buildRoot = getBuildWorkingDir();
        if (buildRoot == null) {
            return;
        }
        IPath moduleOutputPath = buildRoot.append(moduleRelativePath);
        IFolder folder = project.getFolder(moduleOutputPath);
        if (folder.exists()) {
            try {
                folder.delete(true, new SubProgressMonitor(monitor, 1));
            } catch (CoreException e) {
                // JABA added some logging
                Common.log(new Status(IStatus.INFO, Const.CORE_PLUGIN_ID, "Folder deletion failed " + folder.toString(), //
                        e));
            }
        }
    }

    protected void updateMonitor(String msg) {
        if (monitor != null && !monitor.isCanceled()) {
            monitor.subTask(msg);
            monitor.worked(1);
        }
    }

    /**
     * Return the configuration's top build directory as an absolute path
     */
    public IPath getTopBuildDir() {
        return getPathForResource(project).append(getBuildWorkingDir());
    }

    /**
     * Process a String denoting a filepath in a way compatible for GNU Make rules,
     * handling windows drive letters and whitespace appropriately.
     * <p>
     * <p>
     * The context these paths appear in is on the right hand side of a rule header.
     * i.e.
     * <p>
     * <p>
     * target : dep1 dep2 dep3
     * <p>
     *
     * @param path
     *            the String denoting the path to process
     * @throws NullPointerException
     *             is path is null
     * @return a suitable Make rule compatible path
     */
    /* see https://bugs.eclipse.org/bugs/show_bug.cgi?id=129782 */
    public String ensurePathIsGNUMakeTargetRuleCompatibleSyntax(String path) {
        return escapeWhitespaces(ensureUnquoted(path));
    }

    /**
     * Strips outermost quotes of Strings of the form "a" and 'a' or returns the
     * original string if the input is not of this form.
     *
     * @throws NullPointerException
     *             if path is null
     * @return a String without the outermost quotes (if the input has them)
     */
    public static String ensureUnquoted(String path) {
        boolean doubleQuoted = path.startsWith("\"") && path.endsWith("\"");
        boolean singleQuoted = path.startsWith("'") && path.endsWith("'");
        return doubleQuoted || singleQuoted ? path.substring(1, path.length() - 1) : path;
    }

    @Override
    public void initialize(int buildKind, IConfiguration cfg, IBuilder builder, IProgressMonitor monitor) {
        // Save the project so we can get path and member information
        this.project = cfg.getOwner().getProject();
        if (builder == null) {
            builder = cfg.getEditableBuilder();
        }
        try {
            projectResources = project.members();
        } catch (CoreException e) {
            projectResources = null;
        }
        // Save the monitor reference for reporting back to the user
        this.monitor = monitor;
        // Get the build info for the project
        // info = info;
        // Get the name of the build target
        buildTargetName = cfg.getArtifactName();
        // Get its extension
        buildTargetExt = cfg.getArtifactExtension();
        try {
            // try to resolve the build macros in the target extension
            buildTargetExt = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(buildTargetExt,
                    "", " ", IBuildMacroProvider.CONTEXT_CONFIGURATION, builder);
        } catch (BuildMacroException e) {
            /* JABA is not going to write this code */
        }
        try {
            // try to resolve the build macros in the target name
            String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(buildTargetName,
                    "", " ", IBuildMacroProvider.CONTEXT_CONFIGURATION, builder);
            if (resolved != null) {
                resolved = resolved.trim();
                if (resolved.length() > 0)
                    buildTargetName = resolved;
            }
        } catch (BuildMacroException e) {
            /* JABA is not going to write this code */
        }
        if (buildTargetExt == null) {
            buildTargetExt = "";
        }
        // Cache the build tools
        config = cfg;
        this.builder = builder;
        initToolInfos();
        // set the top build dir path
        topBuildDir = project.getFolder(cfg.getName()).getFullPath();
        srcEntries = config.getSourceEntries();
        if (srcEntries.length == 0) {
            srcEntries = new ICSourceEntry[] {
                    new CSourceEntry(Path.EMPTY, null, ICSettingEntry.RESOLVED | ICSettingEntry.VALUE_WORKSPACE_PATH) };
        } else {
            ICConfigurationDescription cfgDes = ManagedBuildManager.getDescriptionForConfiguration(config);
            srcEntries = CDataUtil.resolveEntries(srcEntries, cfgDes);
        }
    }

    private void initToolInfos() {
        toolInfos = PathSettingsContainer.createRootContainer();
        IResourceInfo rcInfos[] = config.getResourceInfos();
        for (IResourceInfo rcInfo : rcInfos) {
            if (rcInfo.isExcluded())
                continue;
            ToolInfoHolder h = getToolInfo(rcInfo.getPath(), true);
            if (rcInfo instanceof IFolderInfo) {
                IFolderInfo fo = (IFolderInfo) rcInfo;
                h.buildTools = fo.getFilteredTools();
                h.buildToolsUsed = new boolean[h.buildTools.length];
                h.gnuToolInfos = new ArduinoManagedBuildGnuToolInfo[h.buildTools.length];
            } else {
                IFileInfo fi = (IFileInfo) rcInfo;
                h.buildTools = fi.getToolsToInvoke();
                h.buildToolsUsed = new boolean[h.buildTools.length];
                h.gnuToolInfos = new ArduinoManagedBuildGnuToolInfo[h.buildTools.length];
            }
        }
    }

    private ToolInfoHolder getToolInfo(IPath path) {
        return getToolInfo(path, false);
    }

    private ToolInfoHolder getFolderToolInfo(IPath path) {
        IResourceInfo rcInfo = config.getResourceInfo(path, false);
        while (rcInfo instanceof IFileInfo) {
            path = path.removeLastSegments(1);
            rcInfo = config.getResourceInfo(path, false);
        }
        return getToolInfo(path, false);
    }

    private ToolInfoHolder getToolInfo(IPath path, boolean create) {
        PathSettingsContainer child = toolInfos.getChildContainer(path, create, create);
        ToolInfoHolder h = null;
        if (child != null) {
            h = (ToolInfoHolder) child.getValue();
            if (h == null && create) {
                h = new ToolInfoHolder();
                child.setValue(h);
            }
        }
        return h;
    }
}
